位图排序
输入:一个最多包含一百万(10^6)个正整数的文件,每个数都小于n,其中n=一千万(10^7)。输入文件中没有重复的整数。
输出: 按升序排列这些数,并写入磁盘。
约束:有 1MB多(不超过2MB) 的内存空间可用,有充足的硬盘空间
一、原理
这道题是在编程珠玑上看到的,是习题3,自己写了一下玩玩,感觉这个算法很棒,通俗易懂。
这个问题的最好算法应当是位图:由于整数没有重复,那么可以用比特位来表示整数的存在。
比如集合 {1,2,5,4,6},
可以用位图表示为 01101110,
由于位图的第1,2,4,5,6为都是1,表示集合里存在着这几个数。
因为这个算法实现的时候,数最大为10 000 000,那么需要用10 000 000个bit的数组来表示,内存大小为 10 000 000/8/1024/1024约等于1.2M
实现需要解决以下问题有:
1) 找到数据i所对应的字节位置
2)找到数据i对应的字节中位位置
3) 判断某位是否为1, 置某位为1
解决它:
1) 找到 对应字节位置: 32系统,相当于 i/32, 使用位操作 数据i >> 5
2) 找到对应字节的位位置: 32系统相当于 i%32, 使用位操作 数据 i&0x1F(31)
附:(m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1) ,这里32为2^5,所以是i&0X1F)
n=2^x 写成二进制就是1后面x个0,m%n就是m的低x位。
n-1=2^x-1 写成二进制是x个1,m&(n-1)也是取m的低x位。
3)数字 a 的 第i位 是1: 方法 a & (1 << i) ,将数据a的第 i位 置1: a | (1 << i)
二、代码
(因为C++STL中有bitset这一容器,所以实现起来并不难,我先用bitset实现,后用C++不用bitset实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | /************************************** * * * -*- coding: utf-8 -*- * * @Date : 2016-05-12 18:54:47 * * @Author : Zeroinger * * * *************************************/ #include <iostream> #include <algorithm> #include <time.h> #include <bitset> #include <iostream> #include <cstring> #include <cmath> #include <queue> #include <stack> #include <list> #include <map> #include <set> #include <sstream> #include <string> #include <bitset> #include <vector> #include <cstdio> #include <algorithm> #include <fstream> using namespace std; #define MAXN 10000000 #define NUM 1000000 #define SHIFT 5 #define MARK 0x1F #define DIGITS 32 int a[MAXN+1]; //bitset <MAXN+1> bit; int b[1+MAXN/32]; void st( int i) { b[i >> SHIFT] |= (1 << (i & MARK)); } int test( int i) { return b[i >> SHIFT] & (1 << (i & MARK)); } void clr( int i) { b[i >> SHIFT] &= ~(1 << (i & MARK)); } void write() { for ( int i=0; i<=MAXN; i++) { a[i]=i+1; } } int getrand( int a, int b) { return ((a+RAND_MAX * rand () + rand ()) % (b-a+1)); } void getdata() { for ( int i=0; i<NUM; i++) { int t=getrand(i,MAXN); swap(a[i],a[t]); } FILE *fp; fp = fopen ( "test_before.txt" , "w" ); if (fp == NULL) { cout << "随机数文件生成失败,error in make_data()." << endl; } int cc=0; for ( int i = 0; i < NUM; i++) { fprintf (fp, "%7d " , a[i]); cc++; if (cc%20==0) fprintf (fp, "\n" ); } cout<<cc<<endl; fclose (fp); cout << "随机数文件生成成功." << endl; } int main() { // bit.reset(); for ( int i=0;i<MAXN;i++) { clr(i); } write(); getdata(); FILE *fpsrc; fpsrc = fopen ( "test_before.txt" , "r" ); if (fpsrc == NULL) { cout << "打开文件失败" << endl; return 0; } int data; while ( fscanf (fpsrc, "%d" , &data) != EOF) { // bit.set(data,1); st(data); } FILE *fpdst; fpdst = fopen ( "test_after.txt" , "w" ); if (fpdst == NULL) { cout << "打开文件失败" << endl; return 0; } int c=0; for ( int i = 0; i <MAXN+1; i++) { if (test(i) /*bit[i] == 1*/ ) { fprintf (fpdst, "%7d " , i); c++; if (c%20==0) fprintf (fpdst, "\n" ); } } cout<<c<<endl; return 0; } |
做人要厚道,转帖请注明出处!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步