题目要求:
给定一个输入文件,包含40亿个非负整数,请设计一个算法,产生一个不存在该文件中的整数,假设你有1GB的内存来完成这项任务。
进阶拓展:如果只有10MB的内存可用,该怎么办?
假设用哈希表来保存出现过的数,如果40亿个数都不同,则哈希表的记录路为 40亿 条。
占用空间统计:1个32位整数占用 4B,40亿个占用 40* 100 000 000 *4B ≈ 16 GB
如果用位方式(俗称位图)统计:40亿/8 B = 5* 100 1000 1000 ≈ 0.5 GB
占用大小 0.5 GB , 对应的bit数组的长度 0.5*1024*1024*1024*8 = 4 294 967 295 (42亿)
具体实现:
创建一个4 294 967 295长度的bit数组 bitArr
遍历40亿个无符号数组,把bitArr对应位置的值设置为1,如,遇到1000,bitArr[1000] = 1
遍历完成之后,bitArr数组中,不为1的位置索引就是没有出现的数
位图存储核心:存储的并不是这40亿个数据本身,而是其对应的位置
只有10MB内存,位图也不能搞定了,需要另寻他法。这里我们可以使用分块思想,时间换空间,通过两次遍历来搞定。
确定需要划分块的个数:
40亿个数 需要500MB的空间,如果只有10MB的空间,至少需要50个块。一般划分都是使用2的整数倍,这里划分成64个块比较合理
将2^32(42亿)划分成 64(2^6) 个块,每个块是 232/26 = 2^26 = 67 108 864 个数,如:
一共有 40 亿个数,统计落在每一个区间上的数有多少,至少有一个区间上的数少于 2^26 个
如果落在某区间上的数少于 2^26 个,则该区间中存在没有出现过的数
第一次遍历:
申请长度为 64 的整形数组,countArr[0…63],统计区间i上的数有多少。遍历 40亿 个数,根据遍历值决定哪一个区间上的计数增加。
此时使用的内存为 64*4B = 256B
第二次遍历:
假设第 37 区间上的数少于 2^26
总结:
保证第二次遍历时每个块都能放进限制的空间中
上面例子中,2^23B < 10MB < 224B,223B = 8MB,所以每块大小确定为8MB,分成64块;
所以至少分成 64 块,分成128块、256块也是可以的。
题目要求:
有一个包含 20亿 个全是32位整数的大文件,在其中找到出现次数最多的数,要求内存限制为 2GB。
思路分析
在很多整数中找到出现次数最多的数,通常做法是用哈希表做词频统计,key为整数,value为出现次数
20亿<2^32-1 value不会溢出,一条记录占用内存 4B+4B = 8B
当哈希表记录数为 2亿 条时,需要占用内存 2亿*8B = 1.6GB内存
极端情况,20亿个整数都不同,需要20亿条记录,占用16GB内存,不满足要求
内存溢出解决办法:
根据哈希函数的性质
注:
此处哈希函数自己定义,比如此处可将哈希函数定义为 num % 16,将大文件数据分成16份
类似nginx或者一些负载均横的技术能够做到的那样子,来了一个数据,根据Hash原则,将其打到不同的机器上去
总结
把一个大的集合通过哈希函数分配到多台机器中,或者分配到多个文件里,这种技巧是处理大数据面试时最常用的技巧之一。
但是具体分配多少台机器,分配到多少个文件,在解题时一定要确定下来,面试官指定或者具体的限制
本题中确定分成16个文件,就是根据内存限制 2GB 的条件来确定的
题目:有一个包含 100 亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL
补充问题:某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门 top 100词汇的可行办法
思路分析
使用解决大数据问题的常规方法:
把大文件通过哈希函数分配到机器,或者把大文件拆分成小文件,一直进行这种规划,直到划分的结果满足资源限制的要求。
牢记,哈希函数,分流/分小文件,处理小数量集合
补充问题分析:
还是用分流的思路来处理,加上堆结构和外排序的手段进行处理
处理每一个小文件/每一台机器的时候,通过哈希表统计每种词及其词频
哈希表记录建立完成后,遍历哈希表的过程中使用大小为 100 的小根堆 来筛选出每一个小文件/每一台机器的top100
然后不同机器/不同小文件 之间的top100再进行外排序或者继续利用小根堆,最终求出整个百亿数据量中的top100
总结
对于top k的问题,除用哈希函数分流和用哈希表做词频统计之外,还经常用堆结构和外排序的手段进行处理
题目要求:
32位无符号整数的范围是 0~2^32 - 1(4294967295),现在有 40 亿个无符号整数,可以使用最多1GB的内存,找出所有出现了两次的数。
注:本题可以看做是第一题的进阶问题,这里将出现次数限制在了两次
思路分析
2次出现 bitArr[num2]和bitArr[num2+1]分别设置为 11