资源限制类问题汇总

资源限制类问题汇总

题目1

在这里插入图片描述

思路

这题如果没有资源限制,那么最直观的思路就是使用哈希表来表示每个数字是否出现。40亿个数,哈希表最多需要40亿的空间,每个占4字节,共160亿字节空间,大概16GB内存。
题目要求只能用不超过1GB的内存,那么可以用位图来代替哈希表,用一个2^32长度的bit位来作为哈希表,每个位上的元素为1,表示对应的元素出现过了;否则就是没出现。所需的内存空间就是(2 ^32)/8个字节,也就是2 ^29字节,536,870,912字节,5亿多个字节,大概是0.5个GB,由于40亿个数不可能覆盖满int能表示的范围,所以最终的所需空间是小于536,870,912字节的。当然,也可以用另一种方式计算,之前用哈希表,每个数字是否出现用4个字节表示,现在用位图,每个数字是否出现用1个bit表示,压缩了32倍,所以使用的内存就是16/32 = 0.5GB。

进阶1:

内存限制为10MB和3KB是一种情况,以3KB为例进行讲解。3KB = 3000 字节,也就是最多可以存储一个大小为750的int数组,取小于750的2的最大次幂,也就是512,使用一个长度为512的数组A,32位无符号整数的取值范围是0~2^32-1,共2 ^32个数,(2 ^32)/512 = 8,388,608,对于[0, 8388608)中的数字,每遇到一次[0, 8388608)中的数字,便将数组A[0]的值加1,对于[8388608, 8388608+8388608)中的数字,每遇到一次,便将A[1]的值加1,…,如果正好有2 ^32个数,且它们都各不相同,那么最终数组A中的每个元素的结果都会是8388608。
但是题目中只有40亿个数,小于2 ^32,这说明即使这40亿个数全不相同,A数组中最终一定存在一个元素,其值小于8388608,记为j,则说明[8388608 * j,8388608 * (j+1))这个区间中的数不全,但是由于3KB的内存限制,仍无法操作这个范围的数据。所以,让8388608/512=16384,把[8388608 * j,8388608 * (j+1))继续分,对于A中的元素,A[i]统计范围[16384 * i,16384 * (i+1))中数字出现的次数。最终,A中一定有一个元素值小于16384,那么便将这个范围的数取出来,按照上面的思想重复进行。知道数据的范围可以完整放入内存进行统计了,便直接找出未出现的一个数就可以了。

进阶2

使用二分的思想,定义三个变量,L,M,R,L代表左边界,初始为0;R代表右边界,初始为2^32-1;M代表中间位置,M初始是2 ^31-1。定义一个计数变量a
首先,遍历一遍数据,如果数据出现在[0,M]上,则a加1,遍历完一遍之后,就得到a的值,如果a<2 ^31,说明文件中[0,2 ^31-1]范围上有缺失的数,那么将右边界改为2 ^31-1,更新M,继续找;如果a>=2 ^31,说明文件中[2 ^31,2 ^32-1]范围上有缺失的数,因为此时,[0,2 ^31-1]范围有没有缺少的数我们并不知道,但是可以知道的是,总共40亿个数,[0,2 ^31-1]范围上出现了超过2 ^31个数,那么[2 ^31,2 ^32-1]范围上的数肯定是少于2 ^31个的,那么就改左边界为2 ^31-1,更新M,继续找

这种方法,很快就可以找到,因为数的范围是0~2 ^32-1,总共2 ^32个数,最多二分32次就可以了。
这种方法也是上面那种方法的极限版本

题目2

在这里插入图片描述

思路

和上面的思路类似,只不过,这次使用两个bit来表示一个数,具体来说,对于某个数k,00是初始状态,如果k在遍历过程中第一次出现,那么00变为01,如果k第二次出现,那么01变成10,如果k第三次出现,10变成11;如果后面k仍出现,那么就不再变化了。最终,所有表示为10的数,就是出现了两次的数。
上面用1个bit位去表示,共需要0.5GB空间,这里用两个bit位去表示,需要1GB的空间,满足条件。

题目3

在这里插入图片描述

思路

这题也可以改成最多只能使用3KB的内存。下面按照3KB内存的情况来说明。
首先,这里的中位数有一些不一样,如果数组的长度是奇数,那么这题的中位数就是平常的中位数;如果数组的数量是偶数,那么这里只取上中位数(也就是两个数中的第一个,不再取两个数的平均值了)。
3KB = 3000 字节,也就是最多可以存储一个大小为750的int数组,取小于750的2的最大次幂,也就是512,使用一个长度为512的数组A,32位无符号整数的取值范围是0~2^32-1,共2 ^32个数,(2 ^32)/512 = 8,388,608,对于[0, 8388608)中的数字,每遇到一次[0, 8388608)中的数字,便将数组A[0]的值加1,对于[8388608, 8388608+8388608)中的数字,每遇到一次,便将A[1]的值加1,…。
遍历过之后,对于40亿个数求中位数,实际上就是求排序之后排在第20亿位置上的数字。假设A[0] = k,……,A[j] = p,sum(A[0,A[j])<20亿,而sum(A[0,A[j+1])>=20亿。那么中位数就必然存在于A[j+1]所对应的数据范围内,也就是[8388608 * (j+1) , 8388608 * (j+2))这个数据范围。
令sum(A[0,A[j])=P,那么,中位数就是满足[8388608 * (j+1) , 8388608 * (j+2))范围内的所有数据,排序后第(20亿-P)位置上的数,也就是在[8388608 * (j+1) , 8388608 * (j+2))范围内的所有数据中,求第(20亿-P)个数(排序之后的)。
问题相当于被拆解成了子问题,问题的规模缩小了。那么就可以继续这样拆解下去,直到找到最终的目标数字。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值