题目
1)给⼀一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
2)与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
3)给定100亿个整数,设计算法找到只出现一次的整数?
4)给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
5)1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?
6)给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
7)如何扩展BloomFilter使得它支持删除元素的操作?
8)如何扩展BloomFilter使得它支持计数操作?
9)给上千个⽂文件,每个文件大小为1K~100M。给n个词,设计算法对每个词找到所有包含它的文件,你只有100K内存
10)有一个词典,包含N个英文单词,现在任意给一个字符串,设计算法找出包含这个字符串的所有英文单词
思考方向(很重要)
关于海量数据的题目,有两个基本方向:
- 首先看是否可以通过特殊的数据结构解决,比如位图,堆,哈希等,无法解决,思考另一个方向
- 能否通过切割数据,再通过方向1来进行解决,因为对于海量数据来说,往往直接解决有困难的地方都是数据过多空间不够问题,我们可以思考是否能够通过切割结合数据结构来解决
切割数据其实就是分治思想,分治即当一个问题无法一次结局的时候,切分成子问题进行解决。
二叉树的递归遍历就是一种典型的分治;快速排序的快速切割数组也是分治;斐波那契数列的递归求解也是分治;生活中当我们遇到困难的时候,可以将其分解成很多小问题进行依次解决;写代码无从下手的时候,可以切割成若干个功能函数一个一个写,这也是分治。
分治很常见,可以说无处不在。
解题思路
1)查找出现次数最多的IP地址
给⼀一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
分治 + 哈希
- 将数据分割成若干的小份,但不是均分,可以按照某一个哈希函数来切割,将映射值为同一个数的IP分到一个文件去,这样相同的IP会出现在同一个文件内。分的文件个数不能太少,因为难免会出现某一个文件出现的地址多的情况,(我们并不是均分,当均分为100份的话,平均下来也是一个文件1G,如果出现该情况,可能会出现一个log file有2个G甚至更多,有可能存不下),为了避免这种情况,分的份数合适,即使出现几个文件较大,也不会太大。
- 对每个文件依次处理,每个文件用哈希表进行映射,哈希表要构造成
key-value
模型,也就是映射的位置不止存IP地址(key),还要跟一个出现的次数(value),找出出现次数最多的IP并保存。 - 将每个文件中出现次数最多的IP地址,放在一起再选出出现次数最多的IP,此问题解决。
本题的关键在于:使用同一个哈希函数分割IP时,相同的IP只会映射到同一个文件。
2)查找出现次数在前K的IP地址
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
分治 + 哈希
- 同上题一个思路,将文件分割成若干小份。
- 同上题思路一样,用
key-value
哈希表进行映射,不同的地方在于接下来的处理,针对每个文件的哈希表,将其中的value当做要比较的值,运用Top K解决方法,也就是堆来得到该文件内前K个出现次数最多的IP - 将所有文件的出现次数前K个IP地址放在一起,再找出前K个IP地址,就是所要求的IP,至此问题解决。
关于堆实现Top K:有一堆海量数据,需要求最大的K个数,排序太耗时间,这个时候用的即堆,建一个元素个数为K个的堆,由于要找最大的N个数,所以要建小堆(没错就是小堆),然后依次从文件读取数据,当读入数据大于堆顶元素,就取代堆顶元素,然后重新调整为小堆,直至元素读取完成。
Linux命令:不会,还没学,学了补上
3)找只出现一次的整数
给定100亿个整数,设计算法找到只出现一次的整数?
位图扩展
整数再多,也无非42亿9000万个,在这里哈希显然是不行的,耗费的空间太多,此时,位图的优势就体现出来了,节省空间,所以此题可以用特殊的数据结构,也就是位图解决。
看图简单了解位图
关于位图:点击此处了解更多关于位图的信息
我们可以算一笔账:
42亿9000万个位 ≈ 5亿个字节
1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
10的9次字节约为1G,5亿个字节约为0.5G = 512mb
也就是说用位图实现这个题完全可以,再合适不过了,但是一个位不足以解决问题,如果一个数没出现为0,出现一次可以置1,出现两次就无法表示,置1和置0都不合适,所以此题用两位来存一个数的信息。
从头到尾遍历一边数,将其信息存入位图,没出现为00,出现一次为01,出现两次为10,出现三次为11,最后只需要打印信息为01的数即可。
4)找存整数文件交集
给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
思路一:
位图 + 比对
- 用位图存储整数,这个题不需要统计出现的次数,因此只需要一个位即可,需要内存是512MB,只需要把第一个文件内的数存入位图即可。
- 第二个文件依次读取,同位图比较,位图中有信息说明该处为交集,直至读取结束。
思路2:
分治 + 比对
- 整数无非是32位,将其按位切割,对文件1进行切割,第一位(也就是最高位)为1的切割进一个文件,第一位(最高位)为0进入另一个文件;
- 重复第1步,对切割的文件进行再次切割,第1次是第1位,第2次对第2位进行切割,直至切割完毕;
- 文件2每个数进行查找,由于已经进行切割,查找类似于二分查找,效率很高,最坏也不过查找32次。
5)找出现次数为2次的整数
1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?
思想:位图扩展
思路同第三题,只需要打印序列为10的数即可,不做过多赘述。
6)找Query文件交集
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
精确算法
分治 + 哈希 + 比对
- 将两个文件用同一个哈希函数进行切割,同样的query会进入编号相同的文件
- 同样编号的文件取
文件1
切割出来的n号文件
,对每条query进行映射,然后用文件2
切割出来的n号文件
进行逐条查找,看是否存在,求出交集。 - 重复直至所有编号相同的文件比对完毕。
近似算法
布隆过滤器 + 比对
- 将query通过三个哈希函数进行映射,用布隆过滤器存储文件1每条query的信息,由于布隆过滤器是用多个位判断一条信息是否存在,而且该位还可能代表另一条query存在,所以存在误判。
- 文件2的query依次通过哈希函数来查找布隆过滤器中对应的位,看query是否存在,这就是近似算法。
简单了解布隆过滤器
使用布隆过滤
关于布隆过滤器:点此了解布隆过滤器
7)布隆过滤器支持删除操作
如何扩展BloomFilter使得它支持删除元素的操作?
布隆过滤器位存储扩展为整型存储
布隆过滤器由于一个位的状态可能和多条信息关联,删除不能简单的置为0,所以这个时候就需要对当前位置的记录信息条数进行计数,将布隆过滤器扩展成一个int记录信息
每有一条信息进入,找到映射的三个位置,就+ 1
,而不是简单的置1置0
删除操作只需要找到映射的三个位置- 1
即可
8)布隆过滤器支持计数
如何扩展BloomFilter使得它支持计数操作?
布隆过滤器位存储扩展为整型存储
同第七题
9)给词查文件
给上千个⽂文件,每个文件大小为1K~100M。给n个词,设计算法对每个词找到所有包含它的文件,你只有100K内存
倒排索引
- 将n个单词构成一颗二叉搜索树,如果是中文,则先经过分词再构建;
- 对每个文件的每个词进行搜索,如果该词存在,将其文件名挂在对应单词后面,直至文件读取结束;
- 每个节点后所挂的文件信息列表即需要获取的信息
看图了解倒排索引
10)字符串查找
有一个词典,包含N个英文单词,现在任意给一个字符串,设计算法找出包含这个字符串的所有英文单词
字典树
字典树是一种特殊的树,根节点为空,每个节点只有一个字母,如图:
关于字典树的原理仅限于了解,目前还是太菜,等学习了之后我会补上