首先要明确这一类的问题都是海量那个数据类型的问题,对于海量数据我们一般采用分而治之的思路去解决,考官考察的就是你有没有处理海量数据的经验。总结几个常见的海量数据相关的面试,供参考。
有一个存放10GB的ip地址文件,每行一个ip地址,统计这个文件中出现次数最多的100个ip,要求机器内存是1GB
这是最经典的ip海量数据问题,面试过程中也经常会出现,对于这类问题,机器内存限制,首先想到的就是分治思想,先拆解再聚合。
思路
- 回答了先拆解再聚合,基本思路是有了,虽然一般不考察如何写代码,但是里面还有很多细节,面试官会进行挖掘。
- 拆解就是将一个大文件分割成若干小文件,分割不能随意分割,要保证相同的ip尽量落在同一个文件中。(想一想如果随意分割会遇到什么问题)
- 文件分割采用Hash算法,保证相同的ip落在同一个文件中
- 对每个小文件计算出现次数最多的100个ip,这就是转为求topK的问题了
- 最终再对每个文件的topK进行聚合,求出最终的topK问题。
优化
- 极端情况下,重复的ip特别多,可能会存在每个文件的大小会有倾斜,该如何处理?
- 分割成多少个文件合适?你回答的数量不要随便说,而是认真思考之后的答案,要有理论根据凸显出你的思考过程。
延展
- 分割成小文件,插入ip之前进行topk计算 ,其实要不要先计算,取决于ip重复的数量
- 文件分割,考察你对每个文件大小的计算,以及运行速度的考量。
代码
参考:https://blog.csdn.net/qq_42234461/article/details/123568886,这篇博文对ip分割统计做了详细的分析以及代码层度的性能测试。
给定 40 亿个不重复的没排过序的 unsigned int 型整数,然后再给定一个数,如何快速判断这个数是否在这 40 亿个整
思路
方法1:分治法
继续采用分治法,其它问题类似上一个ip,不再过多描述。
方法2:位图法
40 亿个不重复整数,我们用 40 亿个 bit 来表示,初始位均为 0,那么总共需要内存:4,000,000,000b≈512M。
我们读取这 40 亿个整数,将对应的 bit 设置为 1。接着读取要查询的数,查看相应位是否为 1,如果为 1 表示存在,如果为 0 表示不存在。
位图算法可以参考:https://www.cnblogs.com/54chensongxia/p/11591979.html
总结
在海量数字中,判断数字是否存在、判断数字是否重复的问题,要想到位图法,采用位作为单位存储数据可以大大节省空间,位图法是一种非常高效的方法。
搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询串的长度不超过 255 字节。
假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w,但如果除去重复后,则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。(一个查询串的重复度越高,说明查询它的用户越多,也就越热门。)
思路
每个查询串最长为 255B,1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。
方法一:分治法
分治法依然是一个非常实用的方法。
划分为多个小文件,保证单个小文件中的字符串能被直接加载到内存中处理,然后求出每个文件中出现次数最多的 10 个字符串;最后通过一个小顶堆统计出所有文件中出现最多的 10 个字符串。
方法可行,但不是最好,下面介绍其他方法。
方法二:HashMap 法
虽然字符串总数比较多,但去重后不超过 300w,因此,可以考虑把所有字符串及出现次数保存在一个 HashMap 中,所占用的空间为 300w*(255+4)≈777M(其中,4表示整数占用的4个字节)。由此可见,1G 的内存空间完全够用。
思路如下:
首先,遍历字符串,若不在 map 中,直接存入 map,value 记为 1;若在 map 中,则把对应的 value 加 1,这一步时间复杂度 O(N)。
接着遍历 map,构建一个 10 个元素的小顶堆,若遍历到的字符串的出现次数大于堆顶字符串的出现次数,则进行替换,并将堆调整为小顶堆。
遍历结束后,堆中 10 个字符串就是出现次数最多的字符串。这一步时间复杂度 O(Nlog10)。
方法三:前缀树法
方法二使用了 HashMap 来统计次数,当这些字符串有大量相同前缀时,可以考虑使用前缀树来统计字符串出现的次数,树的结点保存字符串出现次数,0 表示没有出现。
思路如下:
在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数加 1,否则为这个字符串构建新结点,构建完成后把叶子结点中字符串的出现次数置为 1。
最后依然使用小顶堆来对字符串的出现次数进行排序。
方法总结
前缀树经常被用来统计字符串的出现次数。它的另外一个大的用途是字符串查找,判断是否有重复的字符串等。
前缀树算法参考:https://fuhailin.github.io/Trie/
题目参考:https://edgar615.github.io/bigdata-interview.html
留一个思考题:40亿QQ号, 1G内存,怎么去重?
提示一下
- 可以采用分治聚合思路
- 可以采用位图法
- 试试布隆过滤器!!!!
答案参考:https://www.cnblogs.com/crazymakercircle/p/17487056.html
总结
- 遇到海量数据,先想分治聚合
- Hash来分割,TopK在聚合
- 判断数字是否存在和重复,采用位图算法
- 字符串出现次数统计常用前缀树