advanced-java/docs/big-data/find-hotest-query-string.md
yanglbme dd2740751e docs: format document with prettier
全面整理项目内容,可读性更佳
2020-09-24 09:54:38 +08:00

2.6 KiB
Raw Blame History

如何查询最热门的查询串?

题目描述

搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询串的长度不超过 255 字节。

假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w但如果除去重复后则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。一个查询串的重复度越高说明查询它的用户越多也就越热门。

解答思路

每个查询串最长为 255B1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。

方法一:分治法

分治法依然是一个非常实用的方法。

划分为多个小文件,保证单个小文件中的字符串能被直接加载到内存中处理,然后求出每个文件中出现次数最多的 10 个字符串;最后通过一个小顶堆统计出所有文件中出现最多的 10 个字符串。

方法可行,但不是最好,下面介绍其他方法。

方法二HashMap 法

虽然字符串总数比较多,但去重后不超过 300w因此可以考虑把所有字符串及出现次数保存在一个 HashMap 中,所占用的空间为 300w*(255+4)≈777M其中4 表示整数占用的 4 个字节。由此可见1G 的内存空间完全够用。

思路如下

首先,遍历字符串,若不在 map 中,直接存入 mapvalue 记为 1若在 map 中,则把对应的 value 加 1这一步时间复杂度 O(N)

接着遍历 map构建一个 10 个元素的小顶堆,若遍历到的字符串的出现次数大于堆顶字符串的出现次数,则进行替换,并将堆调整为小顶堆。

遍历结束后,堆中 10 个字符串就是出现次数最多的字符串。这一步时间复杂度 O(Nlog10)

方法三:前缀树法

方法二使用了 HashMap 来统计次数当这些字符串有大量相同前缀时可以考虑使用前缀树来统计字符串出现的次数树的结点保存字符串出现次数0 表示没有出现。

思路如下

在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数加 1否则为这个字符串构建新结点构建完成后把叶子结点中字符串的出现次数置为 1。

最后依然使用小顶堆来对字符串的出现次数进行排序。

方法总结

前缀树经常被用来统计字符串的出现次数。它的另外一个大的用途是字符串查找,判断是否有重复的字符串等。