位图排序
输入:一个最多包含一百万(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实现)
/************************************** * * * -*- 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; }