问题
有一个10G大小的文件中的数字排序
32位无符号整数的范围是0~4294967295,有一个10G大小的文件,每一行都装着这种类型的数字,整个文件是无序的。给你5G的内存空间,要求请你输出一个10G大小的文件,其中存储了原文件所有数字排序的结果。
本题当然不能用一般的排序算法直接排序,因为一般的归并排序、快速排序等都需要所有的数据全都读到内存中。而本题有10G的数据,5G的内存显然放不下。
解决方法:利用堆。核心思路即利用堆求top K的算法。
1
准备一个用于保存较大值的小根堆,这个小根堆容量有限制(用一个变量记录,如本题根据5GB限制,假设容量限制为size)。再准备一个保存小根堆里词频的map,key是num,value是出现次数。map和小根堆是对应的,任意时刻map中的key和小根堆中的数一定一模一样。
2
遍历这10G的大小文件的数字,依次考虑是否放入小根堆中。在加入小根堆前,先用map查一下该数是否在小根堆中(map中是否有此key)。
2.1
若在,则直接设置该key的value++,就不执行加入小根堆操作了。
2.2
如果不在,那么就要考虑是否加入小根堆:
2.2.1
当堆的大小小于容量限制size时,一个数字直接进小根堆,同时在map中创建好一条key-value记录(该记录初始value为1)。
2.2.2
当堆的大小已经达到容量限制size后,则将当前该数与堆顶的数比较。①若当前的数小于堆顶的数,则不加入小根堆,跳过此数。②若当前的数大于堆顶的数,则弹出堆顶的数,将map中原堆顶数的key-value记录删除,然后将当前的数加入小根堆,同时在map中创建好一条key-value记录(该记录初始value为1)。③不可能出现当前的数等于堆顶的数的情况,因为该情况在情况2.1中。
3
最后,弹出小根堆中所有的数,此时是从小到大排列的最大的size个数(写出到一个文件中)。同时记录好本次弹出的最小数,下次再次遍历,此轮遍历只遍历小于刚才弹出最小数的数,那么遍历完成后就又能获得次大的size个数。如此进行下去,最后一定能让所有数字的排好序。
总结
注意,在上述流程中,小根堆实际上用来存储每轮最大的size个数的!之所以用小根堆,是因为堆顶代表了本轮最大的size个数中的最小那个数,新的数只要比他大,就会顶掉它,加入本轮最大的size个数的队伍中。这就是利用堆求top K(本例中是求top size)的核心思想!
整个过程中,5G内存需要分配给两个结构,即等占空间的一个map和小根堆。如果想多用内存,那么小根堆的容量限制就设大点,那么就有可能能少遍历几次就排好序。
根据以上的叙述应该是可以直接写出代码的。楼主太懒了,就没写了。感兴趣的朋友可以自己写一下😉。