对于海量数据“寻找前K大”,目前总结起来我想一般也就两种方案:堆排序、K选择。
这里需要说明两点:
(1)如果数据量比较大,一般我们认为K>2,如果K<2,遍历一遍即可求,无需过多讨论
(2)如果数据量不是太大,完全可以做K次冒泡来求得
这里我们考虑的是数据量比较大,K>2的情况
当然,还有其他方法,欢迎留言补充。
- 堆排序
网上对于解决海量前K大问题比较常用的方法就是堆排。我们知道,使用堆排序是要求堆可以放入内存啊,这么大的数据,怎么放?这里有一个技巧,内存中并不是放入所有数据的堆,而是放入K大小的堆,我们知道堆调整的复杂度为lgK,使用K大小的堆,文件遍历一遍我们就可以求出前K大,所以复杂度是NlgK,N是词的个数,只不过这个“遍历”是文件操作,系数要比在内存中“遍历”的系数要大一些。
方案:将统计好的词频取K个放入内存,调整为最小堆,注意这里是最小堆,求前K大,比较当前的元素与最小堆当中的最小元素,如果它大于最小元素则替换那个最小元素,这样最后得到的K个元素就是最大的K个。反之,求前K小,用的自然就是最大堆啦。
- K选择
K选择是用于求第K大的元素,其实“求前K大”等价于“求第K大”,对数据运行一次K选择算法,那么前K大的元素不就都有了么。但是问题是K选择也需要放入内存啊,这么大的数据又放不下了,肿么办?是的,又是归并的思想。不过这里为大家提供两种可行的方案,方案1使用归并的思想,方案2是直接用K选择的外排序,几乎不需要内存,此为大雄原创,O(∩_∩)O~。
此外,如果大家有更好的方法,欢迎留言补充,谢谢!
方案1:我当时的想法是,将统计好的词频分段,先在内存中求出每段的前K大,然后各段的前K大有了,再将这几段的前K大放入内存求得最终的前K大
方案2:直接使用文件运行前K大算法。思路是开两个文件,选取轴数后一个文件放入大数,一个文件放小数,同时记录各文件放入的数目,从而直接用文件得到最终的前K大。这样做只是时间上耗费可能要多一些
- 堆排 & K选择的对比分析
这里对两种方法做一个简单的分析:复杂度。
前K堆方法的复杂度为NlgK,N为词总数。
K选择方法复杂度:N + 1/2 + 1/4 + … = 2N. (类似快排:快排是N + N + N + … 共lgN个),这样看起来貌似前K快一点,呵呵,实际上还要考虑系数的问题,例如操作文件比操作内存的系数必然要大,一般来讲:只能说K很小的时候,堆比较快;K很大的时候,前K比较快吧。