题目描述:

搜索引擎会通过日志文件把用户每次检索使用的字符串记录下来,每个查询串的长度为1~255B。假设目前有1000万个记录(这些查询串的复杂度比较高,虽然总数是1000万,但如果出去重复后,那么不超过300万个。一个查询串的复杂度越高,说明查询它的用户越多,也就是越热门的10个查询串,要求使用的内存不能超过1GB.

分析:

每个查询串的最长为255B,1000万个字符串需要占用2.55内存,因此无法将所有的字符串读入内存中处理。

解法:

方法一:分治法

对字符串进行设置一个bash函数,通过这个hash函数把字符串划分到更小的文件中,从而保证每个小文件中的字符串都可以直接加载到内存中处理,然后求出每个文件中出现的次数最多的10个字符串,最后通过一个小顶堆统计出所有的文件中出现次数最多的10个字符串。

解法分析:功能方法可行,但由于要对文件遍历两次且hash函数要被调用1000次所以性能不是很好

方法二:字典法

虽然字符串比较多,但是由于字符串的种类不超过300万个,因此可以考虑把所有字符串出现的次数保存为一个字典中(键为字符串,值为字符串出现的次数)字典所需要的空间为300万*(255+4)=3MB*259=777MB(其中,4表示用来记录字符串出现次数的整数占用4B)。由此可见1G内存空间是足够用的。解题步骤为:

(1)遍历字符串,如果字符串在字典中不存在时,那么直接存入字典中去,键为字符串,值为1.如果已经存在,则相应的值加一。这一步时间复杂度为O(n);

(2)在第一步基础上找出出现频率最高的10个字符串。可以通过小顶堆来完成,遍历字典的前10个元素,并根据字符串出现次数构建一个小顶堆,然后接着遍历字典,只要遍历到的字符串的出现次数大于堆顶字符串的出现次数,就用遍历的字符串替换堆顶的字符串,然后再调整小顶堆;

(3)对所有剩余的字符串都遍历一遍,遍历完成后堆中的10个字符串就是出现次数最多的字符串,这一步时间复杂度为O(Nlog10)

方法三:trie树法

方法二中使用的字典法来统计每个字符串出现的次数。这些字符串有大量相同的前缀时,可以考虑使用trie树来统计字符串出现的次数。可以在树的节点中保存字符串出现的次数,0表示没有出现。具体解法为:在便利的时候,在trie树种查找,如果找到,那么把结点中保存的字符串出现的次数加一,否则为这个字符串构建新的结点,构建完成后把叶子结点中字符串的出现次数置为1。这样遍历完字符串就可以知道每个字符串的出现次数,然后通过遍历这个树就可以找出出现次数最多的字符串。

 posted on 2018-12-15 21:15  CircleYuan  阅读(288)  评论(0编辑  收藏  举报