【题目】
32位无符号整数的范围是0~4,294,967,295, 现在有一个正好包含40亿个无符号整数的文件, 所以在整个范围中必然存在没出现过的数。 可以使用最多1GB的内存, 怎么找到所有未出现过的数?
对于这道题目可以使用位图。42亿的整数需要的存储空间为40亿* 4 Byte = 4 * 4 G = 16G,但如果使用位图只需要16G / 32 = 0.5G内存即可。位图如何使用呢?遍历40亿个整数,如果一个数出现,就把对应位置设置为1,入一个数是7000,就把bitMap[7000]设置为1。遍历完成后,再次遍历位图,如果碰到某一位上的数字为0,就说明这个数字不存在,入bitMap[8000] = 0,就说明8000这个数字不存在,遍历结束后,所有没出现的数字也都找到了。
如果数组过长,甚至于数组长度无法用整数来表示(源数据有几百亿条),可以将原本的长数组进行切片,将其作为二维数组来保存。最好将二维数组弄成一个正方形。这里可以给42亿开方。
【进阶】
内存限制为 10MB, 但是只用找到一个没出现过的数即可
如果只有10MB内存,可以使用分段统计。首先10MB = 1千万Byte = 8千万Bit,即10MB可以统计8千万个数字;总共有42亿数字,42亿 / 8千万 = 53(向上取整后得到53),所以可以将42亿数字分为53份,一份大约占8千万Bit;使用这10MB内存分别对这53份进行词频统计。因为只有40亿个数字,所以当我们统计每一份上的数字个数时,肯定会有至少一个区间上的计数不足8千万,利用这一点,我们就可以找出其中一个没出现的数。
第一次遍历时,先申请长度为53的整形数组,用来统计各个区间上的数有多少。遍历结束后肯定会有至少一个区间上的数字不足8千万,任意选出一个这样的区间。
假设第37个区间的计数不足8千万,所以第二次遍历时:释放之前的空间,使用这10MB内存作为第37个区间的位图。遍历时只关注范围在第37个区间上的数字,对其进行描点。遍历结束后位图上一定存在不为1的位置,该位置代表的数字就是未出现的数字。
【进阶】
内存限制为 1KB, 但是只用找到一个没出现过的数即可
如果只有1kb内存,如果还想继续使用分段统计,发现连分段统计的空间都不够。因为1KB = 8千Bit,42亿 / 8千 = 525 KB个区间,远大于题目给出的1KB限制。
这里我们先不管其他问题,直接使用1kb去做long类型的数组。1个long类型的变量为8Byte,1kb / 8 = 125,所以可以将1kb划分为长度为125的long类型的数组。数字的出现范围为0~4,294,967,295,所以将其分为125个区间,遍历40亿个数字,对这125个区间做词频统计,选出不满的一个区间。一个区间上能表示的数字范围为3360万,对这个区间做位图的话需要 3360万Bit = 5000KB的空间,所以还需要继续划分区间;对于选出的不满的的区间进行记录,只需要记录起始和终止位置即可,释放其他空间;将3360万继续划分为125个区间,一个区间的范围为27万,并将1kb划分为长度为125的long类型的数组,再次遍历40亿个数字,对现在的各个区间做词频统计,选出其中不满的一个区间,因为单独对这个区间做词频统计的话需要27万Bit = 40KB的空间,所以还需要继续划分看区间;将27万继续划分为125个区间,再遍历一次数据,对每个区间做词频统计,选出其中不满的一个,它的范围为2160,单独对这个区间做词频统计的话需要2160Bit = 0.27kb,所以可以对这个区间做位图;再遍历一次数据,对这个位图进行描点,选出位图上为0的位置,输出它代表的数字即可。
【进阶】
内存限制为 只能使用有限个变量, 但是只用找到一个没出现过的数即可
这里的思想其实和上道题目相近,对大范围的数据划分空间即可。因为这里只能使用有限个变量,所以二分。第一次二分时,统计两个区间的词频,选择不满的那一段(如果两个都不满可以随便选择),继续对这一段区间进行二分。。。二分32次之后,剩下的那一个就是没有出现过的数。
【题目】
有一个包含100亿个URL的大文件, 假设每个URL占用64B, 请找出其中所有重复的URL
使用哈希函数将每条URL分配到m台机器上,然后每台机器分别统计分给自己的URL是否重复,同时哈希函数的性质保证了不会将同样的URL分配到不同的机器上;或者将存储100亿跟URL的大文件通过哈希函数分成n个小文件,对每一个小文件再利用哈希表遍历,找出同样的URL;总之,大数据问题一定要记得分流,要么使用哈希函数将大文件内容分配给不同的机器,要么使用哈希函数将大文件拆分成小文件,然后处理小数量的集合。
【补充】
某搜索公司一天的用户搜索词汇是海量的(百亿数据量), 请设计一种求出每天热门Top100词汇的可行办法。
对于topK问题,除了使用哈希函数分流和用哈希表做词频统计外,还经常使用堆结构和外排序的手段进行处理。
问题最开始还是使用哈希分流,把包含百亿数据量的文件分流到不同的机器上。对于每一台机器来说,如果分到的数据量还是很大,可能存在内存不足或者其他问题,可以使用哈希函数把每台机器上的分流文件拆成更小的文件处理。
处理每一个小文件的时候,通过哈希表统计每种词及其词频。
哈希表记录完成后,再遍历哈希表,遍历过程中使用大小为100的大根堆选出一个小文件的top100。每个小文件都这样处理,得到每一个小文件的top100.然后把每一个小文件的top100进行外排或这继续利用大根堆,求出一个机器上的top100,对于各个机器也进行同样的处理,最终求出百亿数据量的top100.
【题目】
32位无符号整数的范围是0~4294967295, 现在有40亿个无符号整数, 可以使用最多1GB的
内存, 找出所有出现了两次的数。
对于这道题目仍然可以使用位图来做。向系统申请一个内存为 4294967295 * 2 Bit的空间,使用两个bit来表示一个数字。如果在遍历时遇到一个数字为num,则将位图中bitMap[num * 2] 和bitMap[num * 2 + 1]设置为01,如果再次遇到数字num,则将bitMap[num * 2] 和bitMap[num * 2 + 1]设置为10,如果第三次遇到num,则将bitMap[num * 2] 和bitMap[num * 2 + 1]设置为11,如果之后还遇到num,则不再进行处理。遍历完成后,再次遍历位图,如果发现bitMap[num * 2] 和bitMap[num * 2 + 1]上的数字为10,则说明num出现了两次。
【补充】
可以使用最多10MB的内存, 怎么找到这40亿个整数的中位数?
可以使用划分区间的方式来处理。
10MB内存可以划分为10MB / 4 的长度的整形数组,即2.5MB长度的整形数组,这里取整为2M长度的整形数组,它占用的内存空间为8MB。将区间数量定为4294967295 / 2M,向上取整是2148个区间。
申请一个长度为2148的无符号整形数组arr,arr[i]表示第i个区间有多少个数。然后遍历40亿个数,如果当前数字为num,则arr[num / 2M]++。遍历结束后,就得到了每一个区间的数字状况,通过累加每个区间的出现次数,可以得到中位数落在哪个区间上。比如前200个区间的出现次数为19.98亿,前201个区间的出现次数为21亿,所以中位数一定是201号区间的第0.02亿个数。
接下来申请长度为2M长度的整形数组countArr,然后遍历40亿个数,此时只关心201号区间上的数字,对这些数字做词频统计,累加countArr[i],当累加和第一次超过或等于0.02亿那么i就是中位数。
总结: