哈喽大家好!我是你们的技术小伙伴小米,今天又来和大家分享一个非常实用的算法题!假设我们现在有1000w个查询记录,这些记录中有很多重复的内容,但去重后大概只剩下300w个。那么问题来了,我们如何在1G内存的限制下,统计出最热门的10个查询串呢?今天我们将会使用两种经典的算法:HashMap法和前缀树法,来实现这个需求。
问题描述
首先,我们有1000w个查询串,虽然看起来数据量巨大,但由于重复度较高,去重后只剩300w个独立的查询串。我们需要从这些查询串中找出最热门的前10个查询串,并且要求使用的内存不能超过1G。
方法一:HashMap法
思路解析
HashMap法是一种直接且有效的方法。我们可以利用HashMap来存储每个查询串及其出现的次数,然后通过小顶堆来维护前10个最热门的查询串。具体的操作步骤如下:
- 创建HashMap:遍历1000w个查询串,若查询串不在Map中,直接存入Map,value设置为1;若查询串已经在Map中,则对应的value加1。
- 构建小顶堆:遍历Map,使用一个10个元素的小顶堆来保存出现次数最多的10个查询串。遍历到一个新的查询串时,如果其出现次数比堆顶的查询串多,则替换堆顶元素,并重新调整小顶堆。
- 结果输出:遍历结束后,小顶堆中的10个元素就是出现次数最多的查询串。
内存计算
我们来分析一下内存占用情况。由于去重后的查询串不超过300w个,因此我们需要在HashMap中存储300w个字符串及其出现次数。假设每个字符串的长度为255字节,一个int型变量占用4字节,那么HashMap占用的空间大约为:
- 300w * (255 + 4) ≈ 777MB
由此可见,1G的内存完全足够使用。
代码实现
时间复杂度分析
- 构建HashMap的时间复杂度为 O(N),其中 N 为查询串的总数。
- 遍历Map构建小顶堆的时间复杂度为 O(Nlog10)。
总体时间复杂度为 O(Nlog10),在处理1000w级别的数据时,性能表现不错。
方法二:前缀树法
思路解析
当查询串具有大量相同前缀时,前缀树(Trie)是一种非常适合用来存储和统计字符串的工具。它不仅能够高效地节省内存,还可以通过节点来保存每个字符串的出现次数。
- 构建前缀树:遍历查询串时,依次在前缀树中插入字符串。如果在前缀树中找到该字符串,则将结点的出现次数加1;如果没找到,则构建新结点,并设置出现次数为1。
- 使用小顶堆统计热门字符串:和HashMap法类似,最终通过小顶堆来维护出现次数最多的前10个查询串。
内存计算
由于前缀树的节点共享公共前缀,因此在字符串具有大量相同前缀时,前缀树可以有效节省内存。假设我们有300w个不同的查询串,并且这些串的前缀重合度较高,则构建前缀树的内存占用大概为几百MB,这样加上小顶堆的内存使用,1G的内存应该也是绰绰有余的。
代码实现
时间复杂度分析
- 构建前缀树的时间复杂度为 O(N),其中 N 为查询串的总数。
- DFS遍历前缀树并构建小顶堆的时间复杂度为 O(Nlog10)。
总体时间复杂度仍为 O(Nlog10),但前缀树法在处理有大量相同前缀的字符串时,内存占用会更为优化。
END
通过今天的讲解,我们了解了两种解决热门字符串统计问题的常用方法——HashMap法和前缀树法。HashMap法适合于数据结构简单、查询串相对独立的情况,而前缀树法则更适合于查询串具有大量相同前缀的场景。在实际应用中,选择合适的数据结构和算法可以帮助我们在有限的内存和时间资源下,高效解决大规模数据处理问题。
希望这篇文章能够帮助大家更好地理解和应用这两种算法!如果你有任何问题或建议,欢迎在评论区留言,我们下期见啦!
我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!