您的位置:首页 > 其它

40亿个非负整数中找到没出现的数

2015-09-29 16:55 281 查看
40亿个非负整数中找到没出现的数
题目

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

解答

  对于原问题,如果使用哈希表来保存出现过的数,那么最坏情况下是40亿个数都不相同,那么哈希表则需要保存40亿条数据,一个32位整数需要4B,那么40亿*4B = 160亿个字节,一般大概10个字节的数据需要1G的空间,那么大概需要16G的空间,这不符合要求。

  我们换一种方式,申请一个bit数组,数组大小为4294967295,大概为40亿bit,40亿/8 = 5亿字节,那么需要0.5G空间, bit数组的每个位置有两种状态0和1,那么怎么使用这个bit数组呢?呵呵,数组的长度刚好满足我们整数的个数范围,那么数组的每个下标值对应4294967295中的一个数,逐个遍历40亿个无符号数,例如,遇到100,则

bitArray[100] = 1,遇到9999,则bitArray[9999] = 1,遍历完所有的数,将数组相应位置变为1。

进阶

  如果将上题的内存空间限制改为10MB,但是只用找到一个没出现过的数即可。

解答

  本题将内存空间缩小为10MB,对于40亿个数据来说那是明显不够用的,那么我们只有将数据分块处理,分块应该怎么分,分多少块合理呢?根据我做过的题经验来看,10亿个字节的数据大概需要1GB空间处理(如果这个结论不正确欢迎读者指出),那么10MB内存换算过来就是可以处理1千万字节的数据,也就是8千万bit,对于40亿非负整数如果申请bit数组的话,40亿bit
/ 0.8亿bit = 50,那么这样最少也得分50块来处理,处理每块数据的时候几乎用完了内存空间,这样也不太好。看书上解说是分成了64块,至于为什么是64我目前也不是很了解,我只知道最少50块。所以下面就以64块来进行分析解答吧。

  首先,将0 - 4294967259这个范围平均分成64个区间,每个区间是67108864个数,为了定位更加准确一些,我们先开辟一个大小为64的整型数组intArray,将40亿个数进行区间划分,第0区间(0-67108863)、第一区间(67108864-134217728)、第i区间(67108864*i-67108864*(i+1)-1),......,第63区间(4227858432
- 4294967259)。intArray分别记录每个区间出现的数的个数,肯定至少有一个区间上的计数少于67108864.利用这一点可以快速找出一个没有出现过的数。

第一遍遍历40亿个数过程如下:

1、申请长度为64的整型数组intArray[64],占用内存空间为64*4B;

2、遍历40亿个数,如果当前数为num,则num / 67108864 = i,那么intArray[i]++;

3、然后遍历intArray数组,找出一个计数小于67108864的位置k。

  找出位于某个区间 k 的所有数之后,再从中找出一个没有出现过的数,则问题就解决了。申请一个长度为67108864的bit数组,数组每个位置有两个状态 0和1,如果第 k区间出现的数就赋值为1,没有出现就是0,最后遍历一下bitArray,就可以找到状态是0的一个数,就是没有出现过的那个数。

第二遍遍历40亿个数过程如下:

1、申请长度为67108864的bit数组bitArray[67108864],占用内存空间大约8MB;

2、再遍历40亿个数,这次只需要找出位于区间 k 的所有数,如果 n / 67108864 == k,那么n属于k区间;

3、在第2步的找出的 n,将bitArray[n - 67108864*k]的值置为 1,只做第k区间上数的bitArray映射;

4、遍历完成之后,在bitArray上必然存在状态是0的位置,假设第 i 个位置没被设置成1,那么67108864*k + i这个数就是有个没有出现过的数。

  如此两遍遍历问题就得到解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: