您的当前位置:首页正文

面试--算法--Top K

来源:华拓网

总结:

1、找出一个无序数组里面前K个最大数

插入排序 只找前K个数 或者 建立临时长度为k的数组,然后比较剩余的n-k的数,一次插入

2、假设目前有一千万个记录,有大量重复,请你统计最热门的10个查询串

第一步:外部排序或者Hash函数映射,统计出每个记录重复的次数、
第二步:使用排序算法,找出前10个值

3、海量日志数据,提取出某一天访问百度次数最多的那个IP?

思路:IP是32位,最多2^32个,将其写入大文件中,使用Hash映射的方法,把大文件映射为小文件,统计小文件中的虽大IP,然后汇总比较。

具体:分而治之+Hash

1.IP地址最多有2^32=4G种取值情况,所以不能完全加载到内存中处理;

2.可以考虑采用“分而治之”的思想,按照IP地址的Hash(IP)%1024值,把海量IP日志分别存储到1024个小文件中。这样,每个小文件最多包含4MB个IP地址;

3.对于每一个小文件,可以构建一个IP为key,出现次数为value的Hash map,同时记录当前出现次数最多的那个IP地址;

4.可以得到1024个小文件中的出现次数最多的IP,再依据常规的排序算法得到总体上出现次数最多的IP;

4、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

思路:hash映射成小文件,然后统计小文件频率最大的前100个数,然后归并

5、有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。

方案1:
顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。

找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。

对这10个文件进行归并排序(内排序与外排序相结合)。

方案2:
一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。

方案3:
与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。

应用场景:

  • 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。
  • 假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G

问题解析:

要统计最热门查询,首先就是要统计每个Query出现的次数,然后根据统计结果,找出Top 10。所以我们可以基于这个思路分两步来设计该算法。
即,此问题的解决分为以下俩个步骤:

第一步:Query统计 (统计出每个Query出现的次数)
Query统计有以下俩个方法,可供选择:
1、直接排序法 (经常在日志文件中统计时)
外排序:指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序。

首先我们最先想到的的算法就是排序了,首先对这个日志里面的所有Query都进行排序,然后再遍历排好序的Query,统计每个Query出现的次数了。

但是题目中有明确要求,那就是内存不能超过1G,一千万条记录,每条记录是255Byte,很显然要占据2.375G内存,这个条件就不满足要求了。

让我们回忆一下数据结构课程上的内容,当数据量比较大而且内存无法装下的时候,我们可以采用外排序的方法来进行排序,这里我们可以采用归并排序,因为归并排序有一个比较好的时间复杂度O(NlgN)。

排完序之后我们再对已经有序的Query文件进行遍历,统计每个Query出现的次数,再次写入文件中。

综合分析一下,排序的时间复杂度是O(NlgN),而遍历的时间复杂度是O(N),因此该算法的总体时间复杂度就是O(N+NlgN)=O(NlgN)。

2、HashTable法 (这种方法统计字符串出现的次数非常好)

维护一个Key为Query字串,Value为该Query出现次数的HashTable,每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内完成了对该海量数据的处理。

第二步:找出Top 10 (找出出现次数最多的10个)

  • 算法一:普通排序
  • 算法二:部分排序
  • 算法三:堆

第一步、先用Hash表统计每个Query出现的次数,O(N);
第二步、采用堆数据结构找出Top 10,N*O(logK);
所以,我们最终的时间复杂度是:O(N) + N'*O(logK)。(N为1000万,N’为300万)。