超大文件内单词排序(文件大小大于内存)

Description:

文件200G,内部存储着单词,单词之间以逗号分隔,请生成一个有序文件,对所有单词按照字典序排列。限制你使用的用于存储文件内容的内存大小为1G。


Method1:

(面试时临时想了出来,虽然不是最好的方法,但是只要work,面试就算通过!)

先遍历文件,将以a头的单词找出来,对这些单词进行排序并保存,然后找b开头的单词,依次类推,最后将各个小文件组合。如果找的过程中发现以a开头的单词太多,则继续分成以aa开头,以ab开头...。

另外,例如,  为了防止以a开头的单词过多,可以设置一个阈值,例如10000个单词或者1G,当遍历过程中找到的以a开头的单词大于1000个 或者 以a开头的单词构成的文件将会大于1G时,则放弃,转而,先找以aa开头的....

 

Method2:

针对这种问题有一种经典的方案: 外排序
一般来说,外排序处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。

外排序通常采用的“排序-归并”的策略。在排序阶段,先读入能放在内存中的一定大小的数据量,将其排序输出到一个临时文件,依此进行,最后得到多个有序的临时文件。之后在归并阶段将这些临时文件组合为一个大的有序文件。

例如,如果限制你所使用的内存的大小为1G, 对于20G的文件进行排序, 则, 每次读入1G内容,以某种常规方式在内存中完成排序(快速排序、堆排序、归并排序等等),然后,输出为一个内部数据有序的临时文件存到磁盘。最后对20个有序文件进行归并。

归并时也必须考虑内存大小的限制:

读入每个临时文件的前1/(20+1) G的内容到内存,之所以20+1, 是因为要预留缓冲区用于存放归并的结果,相当于将可用内存空间分成了21份,其中20份用于存储从每个临时文件中读入的内容,剩余的一份用于保存中间结果。 (实践中,将输入缓冲适当调小,而适当增大输出缓冲区能获得更好的效果)。 

Note: 根据归并算法,归并排序时必须同时考虑所有的临时文件。

执行20路归并算法,将结果输出到输出缓冲区。一旦输出缓冲区满,将缓冲区中的数据写出至目标文件,清空缓冲区。一旦20个输入缓冲区中的一个变空,就从这个缓冲区关联的文件,读入下一个1G数据,除非这个文件已读完。

这是“外归并排序”能在主存外完成排序的关键步骤 -- 因为“归并算法”(merge algorithm)对每一个大块只是顺序地做一轮访问(进行归并),每个大块文件不用完全载入主存!

=

Other:

为了增加每一个有序的临时文件的长度,可以采用置换选择排序(Replacement selection sorting)。它可以产生大于内存大小的顺串。具体方法是在内存中使用一个最小堆进行排序,设该最小堆的大小为M。算法描述如下:

初始时将输入文件读入内存,建立最小堆。
将堆顶元素输出至输出缓冲区。然后读入下一个记录:
若该元素的关键码值不小于刚输出的关键码值,将其作为堆顶元素并调整堆,使之满足堆的性质;
否则将新元素放入堆底位置,将堆的大小减1。
重复第2步,直至堆大小变为0。
此时一个顺串已经产生。将堆中的所有元素建堆,开始生成下一个顺串。
此方法能生成平均长度为 2M的顺串,可以进一步减少访问外部存储器的次数,节约时间,提高算法效率。

 

Ref:

https://www.cnblogs.com/lightwindy/p/9650736.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章