空间限制问题

题目一:只用2G内存在20亿个整数中找到出现次数最多的数

有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数。
内存限制为2G

分析一:

  • 对于在很多数中找到出现次数最多的数这种问题,通常的做法是使用哈希表对出现的每一个数做词频统计,哈希表的key为对应的整数,value为该整数出现的次数。
  • 就本题来说,假设一个数出现了20亿次,用32位的整数可以表示该次数不会产生溢出,所以哈希表的key为4B,value为4B,则哈希表的一条记录占用了8B。当哈希表中的记录个数为2亿个时,至少需要1.6GB的内存
  • 如果20亿个数中不同的数超过2亿个,最极端的情况是20亿个数都不相同,那么所占内存为20亿*8B,很显然内存是不够用的。
  • 解决办法是把包含20亿个数的小文件用哈希函数分成16个小文件,根据哈希函数的性质,同一种数是不可能被散列到不同的小文件中。假设哈希函数足够优秀,那么每个小文件中不同的数一定不会大于2亿种。
  • 然后对每一个小文件用哈希表来统计每个数出现的次数,因此得到16个小文件中各自出现次数最多的数和相应的次数统计。
  • 最后只要在16个数中选出出现次数最多的那个即可。
  • 之所以分成16个小文件是与内存限制为2G对应的。

把一个大的集合通过哈希函数分配到多台机器中,或者分配到多个文件里,是处理大数据问题常用的方法,具体分配多少台机器,多少个文件根据内存的限制来确定。
比如:

  • 有一个包含100亿个url的大文件,假设每个url占用64B,请找出其中所有重复的url。
  • 解决办法:
    • 通过哈希函数将大文件中的url哈希到若干台机器中或者单机上拆分成若干个小文件(哈希函数的性质决定了同一条url不会被分配到不同的机器或者不同的文件中),具体的数目根据资源的限制来选择。
    • 然后对每一个小文件再利用哈希表遍历,找出重复的url。

题目二:40亿个非负整数中找到未出现的数

32位无符号整数的范围是0~232=4294967295,现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然有未出现过的数。可以使用最多1G的内存,怎么找到所有未出现过的数?
进阶: 内存限制为10MB,但是只用找到一个没出现的过的数即可。

分析二:

如果40亿个数都不相同,利用哈希表存储则需要40亿*4B=160亿B=16GB的空间,不符合要求。
改进方法:

  • 申请一个长度为232大小的bit类型的数组,232bit=229B=512MB
  • 遍历40亿个数,将对应位置置为1,比如如果该数为7000,就把bitArr[7000]置为1。
  • 然后遍历bitArr数组,如果该位置为0,则证明该数未出现。

进阶问题:将0~232 这个范围平均分成64个区间,每个区间226个数,如果某一个区间上的数少于226那么肯定存在一个数没有出现。

  • 第一次遍历40亿个数,先申请长度为64的整型数组count0~63],count[i]表示区间i上的数有多少。当遍历到第i个数num时,num/226即为num所在的区间,则相应的将该区间计数加1,即count[num/226]++
  • 第二次遍历count数组,若第i个位置上的值小于226,那么该区间肯定存在一个数没有出现,所需内存为64*4B
  • 假设第i区间的数小于226,那么申请长度为226的bit数组,再次遍历40亿个数,此次只关注落在i区间上的数,即num/226等于i的数,将bit[num-226*i]置为1.
  • 遍历bit数组,不为1的位置index即为没有出现的数226*i+index

题目三:搜索词汇的TOPK问题

某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门TOP100词汇的可行方法。

分析三:

  • 把包含百亿数据量的词汇文件哈希到不同的机器上。
  • 对于每一台机器,如果分到的数据量依然很大,可以再用哈希函数把该机器上的大文件拆分成更小的文件处理。
  • 处理每一个小文件的时候,通过哈希表存储每种单词以及相应的出现次数;
  • 遍历哈希表,通过建立小根堆找出每个小文件中的top100;
  • 对每个小文件中的top100排序之后通过外排序的方法选出每台机器上的top100;
  • 不同机器之间的top100再进行外排序或者建小根堆的方式求出百亿数据量中的top100。
  • 外部排序见https://blog.csdn.net/hahaEverybody/article/details/91125723

题目四:40亿个非负整数中找到出现两次的数

32位无符号整数的范围是0~232=4294967295,现在有40亿个无符号整数,可以使用最多1GB的内存,找到所有出现了两次的数。

分析四:

  • 申请一个长度为232*2的bit类型的数组,约占1GB的空间,用两个位置表示一个数出现的词频。
  • 遍历40亿个无符号数,如果初次遇到num,就把bit[num*2+1]和bit[num]置为01,第二次遇到num则置为10,第三次遇到则置为11,若以后再遇到,发现已经被置为11,就不再做任何设置。
  • 遍历完成之后,再次遍历bit数组,如果发现bit[i*2+1]和bit[i]设置为10,那么i就是出现了两次的数。

题目五:找到40亿个整数的中位数

32位无符号整数的范围是0~232=4294967295,现在有40亿个无符号整数,可以使用最多10MB的内存,怎么找到这40亿个整数的中位数。

分析五:

  • 长度为2M即221个无符号整数占用空间为8MB,所以定义每个区间的整数个数为221,那么区间数量为232/221,向上取整为2148个区间。第0区间为0~221-1,第1区间为221~222-1,第i区间为2Mi~2M(i+1)-1
  • 申请一个长度为2148的整型数组,记录该区间上数字的个数
  • 通过累加每个区间上的数字个数,就可以找到40亿个数的中位数,即第20亿个。如果前k-1个区间上出现的数字个数为19.998亿,但是发现当加上第k个区间上的数字个数之后超过了20亿,说明中位数出现在第k区间上,并且是第k区间上的第0.002亿个数。
  • 接下来申请一个长度为2MB的无符号整型数组,然后遍历40亿个数,此时只关心处在第k区间上的数num,然后将count[num-k*2M]++。
  • 遍历完40亿个数之后,就得到了第k区间上的词频统计结果,最后只在第k区间上找到第0.002亿个数即可。
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值