算法通关村第15关【白银】| 海量数据场景下的热门算法题

1. 从40个亿中产生一个不存在的整数

题目要求: 给定一个输入文件,包含40亿个非负整数,请设计一个算法,产生一个不存在该文件中的整数,假设你有1GB的内存来完成这项任务
进阶: 如果只有10MB的内存可用,该怎么办?

1)使用位图存储大数据

这个基础原理可以看位图基础,位存储的核心就是存储一个数的位置而不是它本身

如果用哈希表来存40亿个数,最坏的情况下40亿个数字都不相同也就是需要40亿*4B的空间,大概是15GB大于所给空间了,这里使用位图来存储数字的位置,可以直接每个数字缩小4B,那就是40亿*1bit空间=500兆。

遍历一遍将碰到的数字对应的位置置为1,再遍历一边找出没被设为1的数

2)使用10MB来存储

这个时候就要用到海量数据的常用处理思路:分批分块处理

我们只有10MB空间,就算用位图来存储也只能存83886080个数字

题目只有一个不存在的数字,并且是非负整数那么大小就在0-2^32-1(4294967295)的范围内可以进行平分为64个区间,每个区间包含67,108,864个数,使用一个长度为64的整型数组 countArr 统计每个区间上的数的数量。如果一个区间上的计数小于67,108,864,说明至少有一个数在该区间内没有出现。

第二次遍历:对于计数小于67,108,864的区间,选择其中一个区间,例如第37区间。为这个区间的所有数构建一个位图 bitArr,大小约为8MB(67,108,864个位,每个位用1或0表示数是否出现)。然后,遍历这40亿个数,只处理落在第37区间内的数,将这些数在 bitArr 中对应的位置标记为1。

 找到未出现的数: 遍历完所有数后,遍历 bitArr。必然会找到一个位置上的位没有被设置为1。假设第i位上的值没被设置成1,那么未出现的数就是 67,108,864 * 37 + i。

 3)如何确定分块区间

在第一次遍历中我们划分了64个区间,为什么是64个呢?这个要根据限制的空间大小来决定,我们有10MB的空间一次能存83886080个数字,而划分的一个区间就应该在83886080以内。

2. 用2GB内存在20亿个整数中找到出现次数最多的数

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

  1. 哈希表统计: 通常,我们使用哈希表来对每个数进行词频统计,其中哈希表的键是整数,值是该整数出现的次数。考虑到内存的限制,每个哈希表记录占用8字节(4字节键 + 4字节值),当哈希表记录数达到2亿时,需要至少1.6GB的内存。

  2. 分块处理: 由于内存有限,一次性处理20亿个数可能会耗尽内存,因此需要分块处理。首先,使用哈希函数将20亿个数分成16个小文件,确保相同的数不会散列到不同的小文件中,同时每个小文件中不同数的数量不超过2亿。

  3. 每个小文件的统计: 对每个小文件,使用哈希表来统计其中每种数的出现次数,并找出每个小文件中出现次数最多的数。

  4. 合并结果: 最后,从16个小文件中选择出现次数最多的数,即每个小文件的出现次数最多的数,再从这16个候选数中选择出现次数最多的数,即整个20亿数据集中出现次数最多的数。

3. 从100亿个URL中查找问题

题目: 有一个包含 100 亿个 URL 的大文件,假设每个 URL 占用 64B,请找出其中所有重复的 URL。

补充问题: 某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门 Top 100词汇的可行办法。

  1. 哈希分流和拆分: 面对大规模数据,首要任务是将数据分流到不同的机器或者将大文件拆分成小文件。这可以通过哈希函数来实现,确保相同的数据不会分配给不同的机器或文件。

  2. 每个分流的处理: 对于每台机器或每个小文件,使用哈希表来记录每种词及其词频。这一步会生成哈希表,其中键是词汇,值是词汇的出现次数。

  3. Top K 选择: 哈希表记录建立完成后,遍历哈希表,并使用一个小根堆(或优先队列)来选择每个小文件的前 K 个词汇(Top K)。这个小根堆的大小通常是 100,但可以根据需求进行调整。

  4. 排序和合并: 每个小文件都有自己的 Top K 词汇,按照词频排序。然后,将每个小文件的排序后的 Top K 词汇进行外部排序,或者继续使用小根堆来合并这些结果。这将得到每台机器上的 Top K 结果。

  5. 全局 Top K 选择: 最后,从每台机器的结果中选择全局的 Top K 结果。这可以通过外部排序或再次使用小根堆来完成。

4. 40亿个非负整数中找到出现两次的数 

题目要求:32 位无符号整数的范围是 0~4 294 97 25,现在有 40 亿个无符号整数,可以使用最多1GB的内存,找出所有出现了两次的数。

可以使用bitmap,申请一个2*4 294 97 25大小的bitmap

第一次出现将bitmap[num*2]和bitmap[num*2+1]的位置设为10

第二次出现将bitmap[num*2]和bitmap[num*2+1]的位置设为01

第三次出现将bitmap[num*2]和bitmap[num*2+1]的位置设为11

以后再碰到bitmap[num*2]和bitmap[num*2+1]的位置为11的就不在设置

遍历结束bitmap[num*2]和bitmap[num*2+1]的位置为01的就是出现了两次的数

你可能感兴趣的:(算法,算法)