算法学习(24):大数据题目的解题技巧

大数据题目的解题技巧

哈希函数可以把数据按照种类均匀分流

万能方法,详见算法学习(17)

布隆过滤器用于集合的建立与查询,并可以节省大量空间

详见算法学习(17)

一致性哈希解决数据服务器的负载管理问题

详见算法学习(17)

利用并查集结构做岛问题的并行计算

详见算法学习(18)

位图解决某一范围上数字的出现情况,并可以节省大量空间

问题1

32位无符号整数的范围是0~4294967295 (0~232-1),现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然存在没出现过的数。可以使用最多1GB的内存,怎么找到所有未出现过的数?


准备一个长度为232的byte类型的数组,它的内存空间大概在500MB,每个位置的下标就代表这个位置上的数字出现或没有,遍历40亿个整数,出现的数字它对应位置上的状态就从0变成1。最后状态为0的就是没出现过的数。

问题2

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


思路1:利用哈希函数分流,输出value是2的数
思路2:位图,两个位代表一个数出现的状态。00代表出现0次,01代表出现1次,10代表出现2次,11代表出现两次以上。最后输出状态是01的数

利用分段统计思想、并进一步节省大量空间

32位无符号整数的范围是0~4294967295 (0~232-1),现在有40亿个无符号整数

内存限制为10MB,但是只用找到一个没出现过的数即可


这种只要求找到一个没出现过的数的情况,可以套用通用步骤不一定是10MB,也可以很小。假设限制的内存是m,那么就申请长度为2k的无符号整型数组,条件满足2k所占用的空间小于m,且k取最大值。40亿个数的大小范围是0~232-1,把这个范围平均分成2k份,每个下标分一份,每一份都是等量的232/k。数组的长度是2k,每个下标对应的值代表自己分到的范围内的数字出现了多少个,遍历40亿个数,哪个下标上对应的值小于232/k,代表它所分到的范围,假设为 2a~2b-1,这个范围内部一定有数字没出现过,那么就把这个范围按照前面的步骤继续平均分成2k份,遍历40亿个数,这次只统计40亿个数在 2a~2b-1 范围内的数,这样新分成的2k份中肯定又有数量不够的,则继续往下细分,最后总会分到一个下标只对应一个数,这时就可以找到哪个具体的数没出现过。

假设只给几个有限的变量要怎么找到一个没出现过的数

二分思想,第一次二分后看左右两边谁的数量不够216,谁就有没出现过的数,接下来就一直二分下去,直到找到具体的一个数字。

可以使用最多10MB的内存,怎么找到这40亿个整数的中位数?

思路与上面一样,假设限制的内存是m,那么就申请长度为2k的无符号整型数组,条件满足2k所占用的空间小于m,且k取最大值。40亿个数的大小范围是0~232-1,把这个范围平均分成2k份,每个下标分一份,每一份都是等量的232/k。数组的长度是2k,每个下标对应的值代表自己分到的范围内的数字出现了多少个,遍历40亿个数。40亿个数的中位数是第二十亿个和第二十亿零一个数的平均数,目的就是找到第二十亿个和第二十亿零一个数。数组从下标0开始,累加下标对应的值,找到满足这样一个条件的下标,即它前面的累加和没有超过20亿,加上它上面储存的数刚好超过20亿(或者它前面的累加刚好是20亿,它上面储存了第二十亿零一个数,这里不额外讨论这种情况了,思路是一样的),记住它下标对应的范围,假设为 2a2^b^-1,那么通过它前面下标对应数字的累加和算出还有多少个数到20亿,假设为n,把这2^a^2b-1范围继续等分成2k份,遍历40亿个数只统计2a~2b-1范围内的数,找到累加和刚好超过n的下标,重复上面的步骤,直到找到确定的一个数。

利用堆、外排序来做多个处理单元的结果合并

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

如果允许失误率,则可以用布隆过滤器做,不允许则可以用哈希分流

堆的应用

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


先用哈希分流,key是搜索词,value是词频(即搜索的此时)。每一个小文件用大根堆组织,先把每个小文件的堆顶都拿出来,单独组成一个大根堆叫总堆。每次从总堆弹出堆顶,这时弹出的key就是全局中词频最大的,当弹出一个时,找到它是哪一个小文件中的,把它从那个小文件删除,然后把它下面一条记录(即这个小文件新的堆顶)进总堆。依次重复前面的步骤100次就可以找到Top100

补充题目(腾讯面试原题)

有一个存有10亿有符号整型数字的文件,它是无序的,给定一定内存(可以大也可以小,与上面分段统计题目的思路类似),要求输出一个文件(硬盘上),把这10亿个数变成有序的。

稍微次一点的解法

假设限制的内存是m,那么就申请长度为2k的有符号整型数组,条件满足2k所占用的空间小于m,且k取最大值。把有符号整型的范围是-2147483647-1~2147483647 (-231~231-1),遍历这10亿个数,统计最小的2k个数(第一次是范围是 -231 ~ -231+2k-1),统计完后按照下标和对应的值(即统计的出现的次数)输出这2k个数,然后统计下一个2k范围(第二次是范围是 -231+2k ~ -231+2k+1-1),统计完输出。依次往下进行,直到所有范围都被统计完。


这种做法时间复杂度很高,所以不好

更优的解

利用大根堆。假设限制的内存是m,那么就在不超过内存的情况下申请可以储存最多条记录(假设为n条)的大根堆,这些记录是key,value结构,key代表数字,value代表出现的次数,大根堆内按key的大小组织内部结构。
按照这样的条件遍历这10亿个数:

  • 当堆内不足n条记录时,依次进堆,遇到堆内出现过的数,堆内这个数的value+1
  • 当堆内满n条记录时:
    (1)当前想要进堆的数大于堆顶元素,不进堆,遍历下一个数
    (2)当前想要进堆的数在堆内出现过,堆内这个数的value+1
    (3)当前想要进堆的数小于堆顶元素,堆顶元素弹出,当前想要进堆的这个数进堆
    (4)直到遍历完10亿个数

当这样的一个流程完成后,设置一个阈值,阈值等于每次遍历完后大根堆顶元素的值,把大根堆内的数排序,然后从小到大,有记录出现的次数(假设这10亿个数的范围是a~b ),所以可以输出这a~n-1范围的所有数。下一次遍历由于有前面设置的阈值,小于阈值的数都忽略不管,这样一直到最后就可以完成排序。

posted @ 2022-08-03 15:48  小肉包i  阅读(42)  评论(0)    收藏  举报