含义
在海量数据找出频率最高的前k个数,或从海量数据中找出最大的前k个数,
1.利用堆找出最大的K个数
首先,先理解下用堆找出最大的K个数的常用解法,例如问题是“从M(M <= 10000)个数中找出最大的K个数”
-
利用最大堆 (适合于M规模不大的场景)
建立一个N=M大小的大顶堆,然后输出根节点之后,将根节点删除,然后再将剩余的元素调整成大顶堆;依次重复K次这个过程,最终就找出了K个最大的数。这实质上就是堆排序的过程。这种方法的时间复杂度为O(K *logM) -
利用最小堆(适用于小内存,大数据)
这是最常用的一种方式,首先建立一个N=K大小的小顶堆,这K个元素可以为M中元素中的任意K个;那么对于剩下的M-K个元素,我们逐个与根节点进行对比。
如果当前元素大于根节点的元素的话,就将当前的元素与根节点的元素进行交换,然后将堆再次进行进行调整成最小堆,重复这个过程,直到比较完剩下的M-K个元素;那么最终的小顶堆对应的K个数,必然是M个数中的最大的K个数。这种方法的时间复杂度为O(M*logK)。
方法2相对方法1而言,所需要的内存空间更小了,方法1建立的堆需要M个元素对应的内存空间,而方法2建立的堆只需要K个元素对应的内存空间,所以在对内存空间有严格要求的情况下,采用方法2会更加好一些。
2.海量数据中找出最大的K个数
-
分治思想
对于1而言,M这个指不能很大;如果M很大的话(比如M = 40亿),就不能直接使用排序来找了。对于这种情况,一般是利用“hash映射+堆”的过程,这里的堆是指的方法2,具体如下:
将40亿个数分成若干小的部分(分成多少部分要看题目对内从空间大小的限制),其中N为分成的部分数;然后对于每个小的部分,如果内存可以一次性读取的话,则利用堆采用方法2,选取每个小部分数据中的TOPK个元素,一共NK个元素,然后继续利用堆,采用方法2从这NK个元素种找出最大的K个元素。 -
全排序 (一般不用,内存不够)
-
局部淘汰法
使用一个长度为K的数组,保存钱K个数据,排序,剩下的M-K个数和数组中的数比较,淘汰最小值。O(n+m^2) -
Hash法
果这1亿个书里面有很多重复的数,先通过Hash法,把这1亿个数字去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间,然后通过分治法或最小堆法查找最大的10000个数。 -
最小堆法
见 1.2
总结:
top K问题很适合采用MapReduce框架解决,用户只需编写一个Map函数和两个Reduce 函数,然后提交到Hadoop(采用Mapchain和Reducechain)上即可解决该问题。具体而言,就是首先根据数据值或者把数据hash(MD5)后的值按照范围划分到不同的机器上,最好可以让数据划分后一次读入内存,这样不同的机器负责处理不同的数值范围,实际上就是Map。得到结果后,各个机器只需拿出各自出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是Reduce过程。对于Map函数,采用Hash算法,将Hash值相同的数据交给同一个Reduce task;对于第一个Reduce函数,采用HashMap统计出每个词出现的频率,对于第二个Reduce 函数,统计所有Reduce task,输出数据中的top K即可。
3.找出100亿个URL中重复的URL
(1)对于这类问题,通常采用的的算法思想是“hash映射+哈希表”;大体而言,将100亿个URL利用哈希函数分成N个部分,每个部分用哈希表进行统计次数,最终找到所有重复的URL。
(2)利用字典树。
4.找出20亿个数中出现次数最多的数
解决这类问题的想和3中的法(1)类似。
5.找出40亿个非负整数中出现0次、1次…N次的数
一般找出非负整数中的出现多少次的数,都是利用BItMap
三、延申
处理海量数据问题方法:
分治/hash映射 + hash统计 + 堆/快速/归并排序;
双层桶划分;
Bloom filter/Bitmap;
Trie树/数据库/倒排索引;
外排序;
分布式处理之Hadoop/Mapreduce。