海量数据小内存!从未出现过的数在哪里

题目

现在有 40 亿个无符号整数,无符号整数的范围是 0 ~ 232-1(42亿+),哪怕 40 亿个数完全不同,在该范围中也总有没有出现过的数,试问如何找到所有该范围中没出现过的数?

要求

1)内存 1G

直接使用位图

常见的存储数据单位有 int,long等,而位图则是采用作为单位来存储数据,使用一个或者多个位来标记某个元素对应的值

位图是通过位数组来表示对应的元素是否存在的。像在本题中无符号整数一共有 232 个,如果使用位数组来表示每个数有没有出现过(0 表示没出现过,1 表示出现过),那么差不多只要花费 500 MB 的空间就可以表示这 232 个数

在这里插入图片描述

那么之后我们只需要遍历这 40 亿个数,如果出现过就将位数组中对应的位置标记为 1,如果从未出现过,那么对应的位一定还是 0

最后位数组中还是 0 的位对应的数就是我们要找的从未出现过的数们

2)内存 3 KB

内存只有 3 KB,但是只需要找到一个没有出现过的数即可

将这 3 KB的内存都拿来申请 int 类型的数组,该数组中元素的个数为 2 的某次方,那么 3 KB最多可以申请到 2 的 9 次方大小(512)的 int 类型的数组

在这里插入图片描述

既然为 2 的某次方,就意味着 512 一定可以被 2 的32 次方整除,那么我们就把 2 的32 次方平均分成 512 份,每一份应该有 2 的 23 次个数

接下来,我们就可以遍历这 40 亿个数,遍历到数字 X,我们就把该数除以 2 的 23 次方,来看看,这个数属于哪个范围,将数组对应的位置的值加一

如果说一共有 2 的 32 次方个数(每个数都是不同的),那么将上面的数进行以上的操作,最后 512 个 int 的值都会是 2 的 23 次方

由于现在只有 40 亿个数,并且只要求找到一个没有出现过的数即可,那就意味着,数组中一定会有一个 int 的值不足 2 的 23 次方

既然如此,就可以将缺少的数定位到该区间内,再将该区间内的值分成 512 份,进行之前类似的操作,每一份就是 2 的 14 次方个数,周而复始,一定能够定位到没有出现过的数

3)内存 有限变量

只提供有限几个变量,如何找到一个没有出现过的数

原理和上面的方法差不多,只不过没有办法申请更多元素的 int 数组罢了,没有办法将 2 的32 次方的数分成更多份,那我们可以进行二分,就有两个区间 [0,231-1] 和 [231,232-1]

如果只有 40 亿个数,将其遍历,找两个变量分别统计两个区间出现的数的个数,最后找到一个没有满 2 的 31 次方的区间,继续二分,那么最多分 32 次,一定能够找到一个没出现过的数

举例

如果理解有困难,我们可以将数量变小,进行举例

比如一共有 9 个数(5,8,2,0,3,2,9,4,3),这 9 个数都在 [0,9] 这个范围内,那么至少有一个数在范围内但是不在提供的 9 个数中,请找出没有出现过的数

按照位图的解法,就相当于内存足够让我们申请一个有 10 位的数组

在这里插入图片描述

依次遍历这 9 个数,如果出现就将对应的位的位置标记成 1,就像下面这样

在这里插入图片描述

遍历完成后,就会发现没出现过的数是 1、6、7

在这里插入图片描述

再举另外一个例子

比如一共有 9 个数(5,8,14,0,10,2,9,4,3),这 9 个数都在 [0,15] 这个范围内,那么至少有一个数在范围内但是不在提供的 9 个数中,只提供 20 个字节的内存,请找出一个没有出现过的数

按照申请数组的方法就应该是这样的

一共只有 20 个字节,那么申请的 int 类型的数组最多有 4 个元素(必须是 2 的某次方),如果将提供的 9 个数分别除以 4 来确定每个数应该存在哪个范围,从而数组对应的位置值加一,比如 9,除以 4 就是 2,那么数组第 2 个值加一。

在这里插入图片描述

遍历完成后

在这里插入图片描述

题目只要求找到一个没出现过的数即可,发现数组下标为 0 的值为 3,没有满 4,说明 0~3 这个范围内一定有数没有出现过,将下一轮搜查的范围限制到 0~3,继续之前的操作

在这里插入图片描述

最后可以发现 1 从未出现过

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: 在 python 中,如果要查找海量数据的中位,可以使用 heapq 库中的函 nsmallest() 和 nlargest()。 首先,将所有数据读入内存,然后使用 heapq.heapify() 将数据堆化。接下来,调用 nsmallest() 和 nlargest() 来获取最小的 k 个和最大的 k 个。最后,根据这 k 个的大小关系,就可以得出中位。 例如,如果 k=len(data)//2,那么中位就是最小的 k 个中的最大值。 这种方法的时间复杂度是 O(n log n),空间复杂度是 O(n)。 注意,如果要查找的数据量太大,无法一次性读入内存,就需要使用其他方法,例如流式计算。 ### 回答2: 对于海量数据找中位问题,可以使用堆排序算法来解决。 首先,将海量数据分割成多个小块,每个小块可以使用快速排序算法进行排序,再将排序好的小块使用堆数据结构进行合并。 然后,使用一个最大堆和一个最小堆来存储数据。最大堆保存一半较小的数据,最小堆保存一半较大的数据。具体实现方式如下: 1. 初始化最大堆和最小堆为空堆。 2. 依次读取海量数据,并根据数据的大小来决定插入最大堆还是最小堆。 - 如果最大堆的大小小于最小堆的大小,则将数据插入最大堆,并对最大堆进行调整,保证最大堆的堆顶(即最大值)小于等于最小堆的堆顶(即最小值)。 - 如果最大堆的大小大于最小堆的大小,则将数据插入最小堆,并对最小堆进行调整,保证最小堆的堆顶(即最小值)大于等于最大堆的堆顶(即最大值)。 3. 当读取完全部数据后,根据最大堆和最小堆的大小来决定中位的位置。 - 如果最大堆的大小等于最小堆的大小,则中位为最大堆的堆顶和最小堆的堆顶的平均值。 - 如果最大堆的大小大于最小堆的大小,则中位为最大堆的堆顶。 - 如果最大堆的大小小于最小堆的大小,则中位为最小堆的堆顶。 通过这种方式,不需要将全部数据加载到内存中,可以在读取数据的同时进行排序和求解中位,从而适用于海量数据的情况。 ### 回答3: 在Python中,处理海量数据找中位的一种常见方法是使用堆。 首先,我们需要了解中位的概念。对于一个有序的数据集,中位是指将数据划分为两个等长子集,左子集中的所有元素都小于等于右子集中的所有元素。如果数据集中有奇个元素,则中位是中间的那个元素;如果有偶个元素,则中位是中间两个元素的平均值。 对于海量数据,我们无法直接将其全部加载到内存中进行排序,因此需要使用堆来解决这个问题。堆是一种特殊的树形数据结构,具有以下特点:每个节点的值都大于(或小于)其子节点的值。 我们可以使用两个堆来实现,一个大根堆和一个小根堆。首先,将数据集的前一半数据插入到大根堆中,将剩余的一半数据插入到小根堆中。这样可以确保大根堆中的所有元素都小于小根堆中的元素。然后,我们可以根据数据集的大小,采取不同的策略来计算中位。 如果数据集的大小是奇,中位就是小根堆的堆顶元素。如果数据集的大小是偶,中位就是大根堆的堆顶元素和小根堆的堆顶元素的平均值。 在实际实现中,我们可以使用Python的heapq模块来操作堆。具体的步骤如下: 1. 利用heapq模块的heapify函,将数据集前一半的元素插入大根堆,将剩余的一半元素插入小根堆。 2. 如果数据集的大小是奇,直接返回小根堆的堆顶元素。 3. 如果数据集的大小是偶,返回大根堆的堆顶元素和小根堆的堆顶元素的平均值。 对于海量数据来说,可以将数据分块读取,每次读取一部分数据,然后进行堆的操作。通过这种方式,可以有效地处理海量数据,找到中位

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

富春山居_ZYY(已黑化)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值