资源限制类题目技巧大全

14 篇文章 0 订阅
4 篇文章 0 订阅

文章目录


资源限制技巧

1)布隆过滤器用于集合的建立与查询,并可以节省大量空间
2)一致性哈希解决数据服务器的负载管理问题
3)利用并查集结构做岛问题的并行计算
4)哈希函数可以把数据按照种类均匀分流
5)位图解决某一范围上数字的出现情况,并可以节省大量空间
6)利用分段统计思想、并进一步节省大量空间
7)利用堆、外排序来做多个处理单元的结果合并

题目一:32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,可以使用最多1GB的内存,怎么找到出现次数最多的数?

使用方法四:利用哈希函数进行分流。

因为有40亿个无符号整数,因此如果直接开辟一个数组对其中的所有整数进行排序,那么需要的数组大小是40亿 * 4 =160亿个B,即16G,就超过了题目限定的1G内找到出现次数最多的数

那么我们可以用哈希表,它的大小跟整数出现的次数没有关系,而是跟这40亿个整数的种类有关系。因为一个数再怎么多,用哈希表记录的还是对应的key-value,只不过value在增加。而它们分别是4个字节,加起来就是8个字节。

假设最坏情况下,出现的40亿个无符号整数都不相同,则哈希表的大小为40亿 * 8 = 320亿个B,即32G,还不如直接在数组中排序再统计的内存大小。

我们可以反过来想,1G的内存如果用哈希表对数据进行记录,那么可以记录的条数有:1G约等于10亿B,再用10亿除以8,则记录的条数有1亿两千500条。但是哈希表中除了数据外,还有其它的属性会占用空间,因此为了保守起见,就假设该哈希表就只能装1千万条数据。

因此我们可以用40亿个数 / 1千万条数据=400,当一个数来的时候,就可以将该数字用哈希函数,算出一个哈希值后,模上400,那么最后的范围一定是0~399(文件号)。再将该数字放入最后模的文件号当中。因为哈希函数的均匀性,即使是有40亿种不同的数,最后也会均分地放到各个文件当中

即使有些碰撞,一个文件中的数据多于1千万条,但没有关系。因为在前面我们算过,一个文件真正可以存的数据条数是1亿两千500条。1千万条只是比较保守的一个数,因此一个文件超了1千万条数据也不会有什么,也不会超的了多少。

按照哈希函数的性质,一个相同的东西经哈希函数算出的哈希值一定相同。因此就一个东西就不可能会分配到多个文件当中。那么就可以用一个哈希表依次记录各个文件中出现次数最多的数,记录完一个文件的就释放哈希表的内存空间,再记录下一个文件中出现次数最多的数。到最后,得到的是这400个文件中各个文件的出现最多次数的数,再求其中的出现次数最多的数,就能够得到所有数中出现次数最多的数。(仔细读有点绕,但不难)

推广
当一个超级大文件来的时候,给你一个限制条件,一个文件不能超过多少G,当我们发现如果用哈希表直接进行记录时会超出该限制条件。
解决方法:
同样地,对大文件中的数据用哈希函数求出哈希值再%,得到一个数代表文件号,就放到对应的文件中。但是我们说了如果直接存的话会超出内存限制,因此可以进行二次哈希,对进入小文件的数据用另一个哈希函数求出hash值后,进行小小文件,直到满足题目给定的条件位置。

题目二:32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然存在没出现过的数。可以使用最多1GB的内存,怎么找到所有未出现过的数?

使用方法五:位图解决某一范围上数字的出现情况,并可以节省大量空间。

如果使用Set,则内存会有40亿*4=160亿B=16G,超出了题目给出的内存限制。

我们可以使用位图,创建一个由bit类型组成的数组,其中的一个下标就是1个bit因此可以创建2的32次方个bit大小的数组。又因为8个bit组成一个B,则该数组就一共有2的32次方 / 8 个B,这个是肯定在1G的范围内的,因此就满足了题目的限定条件。

但是对于一个具体的bit(将一个整型当作一个大bit来看,对应到数组中的位置)要对应到数组中的bit,该如何设置?
可以先创建整型数组,大小为10,即创建出了320个bit大小的数组。此时要求出一个具体的i(bit)对应到数组中的哪个bit,则可以用i先除一个32,得到i归属于哪个下标表示bit位。再用i%32求出i在该部分的哪个具体位置。

举个例子:此时有一个i为64,数组是整型数组,大小为10。则arr[0]=0~31,arr[1]=32 ~63,arr[2]=64 ~95,此时64先除32,得到2,则属于2下标的位置。再用64%32=0,则在下标为2的0号bit位。得到该bit位后&1,如果得到的是1,则原本为1状态;如果得到的是0,则原本为0状态。用代码表示为:int status = (arr[i/32]&(1<<(i%32)))==0?0:1;

因此对于一个40亿整数的文件,遍历该文件中的数字,出现一个数字就用哈希函数求出哈希值再对应到数组中的bit位置,再把该位置描黑代表出现过。到最后,如果bit数组中有对应的bit位没有描黑,则对应的位数就是没有出现的数字。

【进阶】内存限制为 3KB,但是只用找到一个没出现过的数即可

使用方法六:利用分段统计思想、并进一步节省大量空间。

因为内存限制为3KB,则为3000B,如果开辟一个整型数组,那么整型数组的长度为3000B / 4B = 750 。这个是刚好不会超出内存范围的。为了让2的32次方个数能够均分到数组中,我们可以将数组的大小一个离750这个数最近的2的几次方的一个数,即512

因此该数组的一个下标代表的范围是2的32次方 / 512 = 8388608,因为40亿个数是在43亿个数的范围内的,则肯定有某些数是没有出现过的。

因为按照题目要求,找到一个没出现的数即可,那么肯定能够找到不满的其中一个下标的范围。再在该范围中去找哪个数字没出现。在该范围中得知有没出现的数字,我们可以再将该范围分成512份,因此在新的小范围中肯定又有不满的小范围,再把不满的新的小范围分成512份,直到定位出没出现的数为止。

【进阶】只用有限几个变量,如何在40亿个无符号整数中找出一个没有出现过的数?

我们可以定义一个L和R,L到0位置,R到2的32次方位置。进行二分,mid就是在中间的位置,划分左右两边为2的31次方那么多数。就必然存在左右两侧不够2的31次方的情况,再在不够的一侧进行二分,到最后一定能把没出现的数二分出来。

总结:在3K内存限制的时候,进行512分。在有限几个变量限制的时候,进行二分

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

首先可以使用布隆过滤器,但是利用它会有失误率。如果不能有失误率,则用哈希函数进行分流的方法。
先对大文件中的数据用哈希函数求出哈希值,分配到小文件当中。如果该小文件不能满足题目的内存限制要求,就对小文件中的数据再次进行哈希,放入到直到满足题目要求的文件中即可。主要的就是利用多次哈希

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

哈希函数可以把数据按照种类均匀分流,将所有词汇按种类分配进入不同的小文件中,然后求出每一个文件中的前100,最后汇总求出总体的前100。

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

可以利用位图,建立一个bit数组。每两个bit代表一个数字的出现次数的情况。假设数组最前两个bit位代表的是0,后面两个bit位代表的是1,可以代表1的两个bit位进行统计出现的次数。如果是00,代表1一次都没有出现;如果是01,代表出现了1次;如果是10,代表出现了两次;如果是11,代表出现了三次或三次以上。出现一个数就人为地设置它代表的两个bit位,出现11之后就无需再管它。

最后遍历数组,找出哪个数对应的bit位是10的,那么就说明它出现了两次。

但是,因为我们是用两个bit位代表一个数字的出现次数,假设用一个bit位的时候,数组的大小为:2的32次方 / 8 / 1000 = 536,870,912M,因为是两bit位,则再乘2,超过了1G。那么如何解决?

可以使用分段加位图的思想。我们可以先统计前半部分的2的31次方哪个数出现了两次,再统计后半部分的2的31次方哪个数出现了两次,最后就能够求出这40亿个数哪些数出现了两次。

题目五:32位无符号整数的范围是0~4294967295,现在有40亿个无符号整数,可以使用最多3K的内存,怎么找到这40亿个整数的中位数?

分段统计思想,将数分为各个区间,然后区间内统计词频,可以知道中位数在哪个区间中,然后再将该区间在分段求解。

同样地,因为是最多3K内存,可以设置一个长度512的数组对2的32次方个数进行划分。在数组中对每个下标代表的数进行统计。如:下标0代表的数是0~8388608

因为要找出40亿个数的中位数,那么要找出第20亿个数是哪个。如果下标0中出现的数有1亿个,那么中位数绝对不可能出现在下标0代表的某个数中。假设统计0到170下标的数有19亿个,到171下标的数有23亿个,那么中位数肯定就是在171下标代表的某个数中。找到171下标中第一亿个数就是中位数

题目六:32位无符号整数的范围是0~4294967295,有一个10G大小的文件,每一行都装着这种类型的数字,整个文件是无序的,给你5G的内存空间,请你输出一个10G大小的文件,就是原文件所有数字排序的结果

先创建一个容器,它是可以排序的,并且能够记录出现的数字与对应的次数。放三条记录。遍历大文件,如果遇到一个数比该容器中出现的最大值小,那么就让容器中的最大值出去,小的数进来。到最后一定是该文件中数字最小的前三名。

例如1出现了1000次,6出现了9万次,13出现了15万次。创建一个大文件,将容器中的数字从小到大并且按统计的次数放多少个,依次放入大文件中。当放完容器中的数后,用一个变量t记住容器中最大的数。当下次遍历大文件的时候,如果出现了小于t的数就不用管统计。重复按照这些步骤,最后的大文件中一定是原来大文件的排序好的结果。

按照题目要求是5G,那么我们可以创建的容器有5G的大小。步骤跟上面的完全一样。容器的结构就是大根堆。

题目七:求一个大文件中出现次数最多的前100名

方法:哈希函数分流+堆上堆的方法。

先将大文件用哈希函数求出哈希值分流到小文件中,在小文件中利用大根堆按照出现的次数进行TOP100的排序。最后从各个小文件中取出自己的TOP1,放入到一个新的大根堆中,在新大根堆中按照出现次数排序,这里的TOP1才是全局的TOP1。假设这个TOP1来自二号文件。再从二号文件的TOP2中入新的大根堆,这次就TOP1就是全局的TOP2,以此类推。

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zjruiiiiii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值