前言:
这本书也是看了别人的博客推荐过来的一本好书,先读了文章的前言,建议我们的学习的速度不要太快,而是要认真思考每一章的东西,要仔细阅读,一次读一章。
大概翻了一下这本书,总体上感觉是比较容易接受的,讨论的问题也都是比较常见的问题,算法方面也没有用到多么高深的算法,但是最大的感触在于这本书教给了我们思考的方式,对于一个问题提出的不同的解决方案,或者从时间的优化,或者从空间上的优化,或者两者之间的一个平衡。对于一些常见的题目,给出了一些非常精巧的解法,这些解法确实是大师级的人物才能想到的,也确实受益匪浅。
关键词:Bit-Map 集合 分治法 排序
本章开头便引出这样一个问题:
一个文件最多包含一千万条记录,每条记录都是7位数的整数,并且无重复,内存只有1M,如何实现排序。
1.计算1千万条记录需要的内存=1000,0000 *4Byte=40M,显然内存不够,不可以一次读入进行直接排序。
2.最先想到的可用分治法,在磁盘上分批读入,然后归并排序。
3.1M内存可以存放100,0000Byte/ 4=25,0000个号码,总共1000,0000个号码,那么可以分40趟来分批排序,比如第一趟排0-249999,遍历磁盘文件,将0到249999之间 的整数读入内存进行排序,然后输出。这样带来的代价是需要40次读取磁盘文件。
4.有木有更优秀的方案,最好能一次读入所有数据,然后经过某种排序算法,一次输出最终的排序结果?
5.于是不得不想,怎么样才能将这一千万的记录存到1M的内存中呢?进一步的思考也就是能不能用大约1M=800万位去表示这一千万的记录呢?
6.Bit-Map便应运而生,看起来很牛逼的一个位图说法,其实很简单,就是用一个长度大约为800万的字符串来表示这一千万的记录,字符串的长度就是能表示的最大整数值-1,如数i存在,那么第i位为1,否则为0,说白了其实就是个集合的向量表示方法。看过算法书的人,应该对这个很了解了,就不详细说了。
7.于是乎,我们的解法也便呼之欲出,就用一个长度为800万的字符串,来表示这1千万的整数,若某个整数存在,那么对应的位就为1了,否则就为0,然后程序输出的时候遍历一下字符串,从第0位开始遍历,若为1,就输出i的值。这样就实现了,一次读入内存,经过一次排序,全部输出结果的方案了,如是第一次见Bit-Map应该是比较吃惊这种处理方式的,至少笔者我是没有想到还可以这样来搞,不得不感叹前人那些大家的智慧啊。
//BitMap -----test #include<iostream> #include<set> //能表示的数的最大值 const int n = 10; using namespace std; int main() { char a[n]; int data[n]; //生成0-9不重复的数字 for(int i = 0; i < n; i++) data[i] = i; //将前6个随机打乱顺序 for(int i = 0; i < 6; i++) { int j = i + int((n - i) * (rand()/(RAND_MAX*1.0))); swap(data[i],data[j]); } //打乱后数据 cout<< "打乱后的前6个数据:" << endl; for(int i = 0; i < 6; i++) cout << data[i] << " "; cout << endl; //对bitmap初始化 //初始bitmap情况 cout<< "初始bitmap情况:" << endl; for(int i = 0; i < n; i++) { a[i] = 0; cout << (a[i] == 0?"0 " : "1 "); } //遍历文件若对应整数存在对应位置为1 cout<< endl << "处理后的bitmap情况:" << endl; for(int i = 0; i < 6; i++) { a[data[i]] = 1; } for(int i = 0; i < n; i++) cout << (a[i] ==0?"0 " : "1 "); cout << endl; //排序 cout << "排序后前6个数据情况:" << endl; for(int i = 0; i < n; i++) if(a[i])cout << i << " "; cout << endl; system("pause"); return 0; }
运行效果:
关于代码的几点说明:
1.如果不缺内存,如何使用一个具有库的语言来实现一种排序算法以表示和排序集合?
排序算法:qsort(a,n,sizeof(int),comp)
排序集合:stl中的set
2.如何使用位逻辑运算实现位向量?