Skip to content

腾讯高频面试题——40亿QQ号,1G内存,怎么去重?

相信不少人在网上都刷到过这个面试题,“40亿QQ号,1G内存,怎么去重??”。 ——这是一个非常典型的海量数据处理问题。

特别是在海量数据处理的场景中,去重是一个非常常见且核心的问题。

面试中,这类问题往往以“在有限内存下处理超大数据量”的形式出现,就是在考察面试者对数据结构、算法、内存管理以及系统设计的综合能力。

这个问题不仅考验对基础知识的掌握,更要求你能在资源受限的条件下,设计出高效且可行的解决方案。

今天就带大家一起从问题分析入手,逐步探讨各种去重方案,从最直观的方法到更高级的位图、布隆过滤器,再到分治策略,详细分析它们的原理、优缺点以及内存消耗,最终给出在1G内存限制下的最佳方案。

1、问题分析与约束条件

首先,我们来仔细分析一下这个面试题的几个关键信息和隐含约束:

  • 数据量:40亿个QQ号。QQ号通常是数字,假设为uint32_t类型(最大值约42亿),或者uint64_t类型(如果QQ号位数较长)。为了简化计算,假设QQ号是32位无符号整数,即范围在0到4,294,967,295之间。
  • 内存限制:1GB(1024MB)可用内存。这是最核心的约束,意味着我们不能将所有QQ号直接加载到内存中进行处理。
  • 目标:去重。即找出所有不重复的QQ号。
  • 隐含条件:
    • 数据来源:QQ号可能存储在一个大文件中,或者通过网络流式传输。通常假设数据是静态的,存储在一个文件中。
    • 处理时间:如果没有明确说明,但在面试中,通常要求解决方案在合理的时间内完成,不能是无限期的。
    • 精确性要求:是否允许一定的误判率?这会影响我们选择的数据结构。
1.1、QQ号的数据特性

如果QQ号是32位无符号整数,那么每个QQ号占用4字节。40亿个QQ号的总大小为:

40亿 * 4 字节 = 160亿 字节 = 16 GB

显然,16GB的数据量远超1GB的内存限制,因此我们无法将所有QQ号一次性加载到内存中进行处理。这要求我们采用外部算法或内存效率更高的数据结构。

1.2、内存限制的挑战

1GB内存意味着我们只能存储大约 1GB / 4字节/QQ号 = 250MB / 4字节/QQ号 = 2.5亿 个QQ号。这进一步强调了我们需要寻找一种不直接存储QQ号本身,或者以极高效率存储QQ号存在性的方法。

接下来,将基于这些约束条件,逐步探讨各种去重方案。

2、基础去重方法及其局限性

在讨论高效解决方案之前,我们先回顾一些常见的去重方法,并分析它们在“40亿QQ号,1G内存”这个场景下的局限性。

2.1、使用哈希表(Hash Table / std::unordered_set

原理:

哈希表是一种非常高效的数据结构,可以实现平均O(1)的插入和查找操作。我们可以遍历所有QQ号,将每个QQ号插入到一个哈希表中。在插入之前,先检查哈希表中是否已存在该QQ号,如果存在则说明是重复的,否则插入。最终哈希表中存储的就是所有不重复的QQ号。

内存消耗分析:

假设每个QQ号是32位无符号整数(4字节)。

一个哈希表除了存储数据本身,还需要额外的开销:

  • 哈希表节点开销:每个节点通常包含数据本身、指向下一个节点的指针(解决哈希冲突,如链地址法)、以及一些管理信息。假设每个节点额外开销为8字节(一个指针)+ 4字节(数据本身) = 12字节。
  • 哈希表本身开销:哈希表通常需要维护一个数组,其大小通常是元素数量的1.5到2倍,以保证较低的冲突率。

假设有N个不重复的QQ号,最坏情况下N接近40亿。那么内存消耗将是:

N * (4字节(QQ号) + 额外开销)

即使我们假设每个QQ号的额外开销只有4字节(非常乐观,实际可能更高),那么存储40亿个不重复QQ号所需的内存为:

40亿 * (4 + 4) 字节 = 40亿 * 8 字节 = 32 GB

这远超1GB的内存限制。即使QQ号数量远小于40亿(例如,只有2.5亿个不重复QQ号),2.5亿 * 8 字节 = 2 GB,仍然超出了1GB的限制。

局限性:

  • 内存溢出:对于40亿这种量级的数据,哈希表需要巨大的内存空间来存储所有不重复的QQ号及其管理信息,这在1GB内存限制下是不可行的。
  • 性能问题:当哈希表过大时,哈希冲突会增加,导致查找和插入性能下降。同时,内存访问模式可能不友好,影响缓存效率。
2.2、使用std::setstd::map(基于红黑树)

原理:

std::setstd::map在C++中通常基于红黑树实现。它们可以自动维护元素的有序性,并在插入时自动去重。插入和查找的平均时间复杂度为O(logN)。

内存消耗分析:

红黑树的每个节点除了存储数据本身,还需要存储:

  • 数据本身:4字节(QQ号)。
  • 颜色信息:通常1位,可以忽略不计。
  • 父节点、左子节点、右子节点指针:每个指针在64位系统上占用8字节,共24字节。

因此,每个节点至少需要 4字节(QQ号) + 24字节(指针) = 28字节。如果考虑节点对齐和额外开销,实际可能更大。

存储40亿个不重复QQ号所需的内存为:

40亿 * 28 字节 = 112 GB

这比哈希表所需的内存还要大得多。

局限性:

  • 内存溢出:与哈希表类似,红黑树的节点开销更大,内存消耗更为严重,远超1GB限制。
  • 性能问题:虽然logN的复杂度在理论上比哈希表的O(1)差,但在实际操作中,红黑树的内存访问模式可能比哈希表更连续,但在数据量巨大时,其性能瓶颈依然是内存访问和比较操作。
2.3、排序后去重

原理:

如果数据可以完全加载到内存中,我们可以先对所有QQ号进行排序,然后遍历排序后的序列,相邻元素相同的即为重复。这种方法的时间复杂度通常为O(N log N)(排序),空间复杂度为O(N)(存储所有数据)。

内存消耗分析:

如前所述,40亿个QQ号需要16GB的存储空间。即使是原地排序算法,也需要至少16GB的内存来存储所有QQ号。

局限性:

  • 内存溢出:无法将所有QQ号一次性加载到内存中进行排序。
  • 不适用于外部数据:标准的内存排序算法不适用于处理超出内存容量的数据集。
2.4、总结

以上这些基础的内存内去重方法,都因为“40亿QQ号”的巨大数据量和“1G内存”的严格限制而无法直接应用。我们需要寻找一种能够以更紧凑的方式表示数据存在性,或者能够分批处理数据的方法。

接下来,介绍几种针对海量数据去重的高效解决方案。

3、位图(Bitmap)解决方案

位图(Bitmap),也称为位数组(Bit Array),是一种非常节省空间的数据结构,特别适用于对大量非负整数进行“存在性”判断的场景。它的核心思想是利用一个比特位(bit)来表示一个整数的“存在”或“不存在”状态。

3.1、位图原理

假设我们要对一系列整数进行去重,并且这些整数的范围是有限的(例如,从0到N)。

可以创建一个足够大的位数组,数组的每个索引代表一个整数,而该索引位置上的比特位的值(0或1)则表示该整数是否出现过。

  • 初始化:创建一个长度为 N+1 的位数组,所有位都初始化为0。
  • 标记存在:当遇到一个整数 X 时,将位数组中索引为 X 的比特位设置为1。
  • 判断重复:当再次遇到整数 X 时,检查位数组中索引为 X 的比特位。如果该位已经是1,则表示 X 已经出现过(是重复的);如果该位是0,则表示 X 是第一次出现,将其设置为1。
  • 提取不重复元素:遍历整个位数组,所有被设置为1的索引对应的整数就是不重复的QQ号。

位图去重原理图

3.2、内存消耗计算

假设QQ号是32位无符号整数,其范围是 02^32 - 1,即大约 4.29 * 10^9

为了覆盖所有可能的QQ号,我们需要一个大小为 2^32 的位数组。

  • 总比特数:2^32 比特
  • 转换为字节:2^32 比特 / 8 比特/字节 = 2^29 字节
  • 转换为GB:2^29 字节 / (1024 * 1024 * 1024) 字节/GB = 2^29 / 2^30 GB = 0.5 GB

所以,使用位图来存储40亿个QQ号的存在性,理论上只需要0.5GB的内存。

与1GB内存限制的比较:

0.5GB远小于1GB的内存限制。这意味着位图方案在内存上是完全可行的。

3.3、位图的优缺点

优点:

  1. 极度节省内存:每个数字只占用1个比特位,相比直接存储数字(4字节)或哈希表(28+字节),内存效率极高。
  2. 效率高:插入和查找操作的时间复杂度都是O(1),只需要进行位运算。
  3. 天然有序:遍历位图时,可以自然地按升序获取不重复的QQ号。

缺点:

  1. 数据范围限制:位图适用于数据范围相对密集且不大的场景。如果QQ号的范围非常稀疏,或者最大值非常大,位图会占用大量内存,即使实际存在的QQ号数量不多。例如,如果QQ号的最大值是 2^60,那么位图将需要 2^60 / 8 字节的内存,这是不可接受的。
  2. 无法存储额外信息:位图只能表示“存在”或“不存在”,无法存储与QQ号相关的其他信息(如QQ号出现的次数)。
  3. 不适用于负数或非整数:位图只能用于非负整数的去重。
3.4、总结

对于“40亿QQ号,1G内存”这个面试题,如果QQ号的范围是32位无符号整数,那么位图是一个非常理想的解决方案,因为它能够以极低的内存消耗(0.5GB)完成去重任务,并且具有O(1)的查找和插入效率。

这是在精确去重且数据范围可控情况下的首选方案。

4、布隆过滤器(Bloom Filter)解决方案

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否可能在一个集合中。它的特点是,如果判断一个元素不在集合中,那么它一定不在;如果判断一个元素在集合中,那么它可能在(存在一定的误判率)。

4.1、布隆过滤器原理

布隆过滤器由一个很长的位数组(Bit Array)和k个哈希函数组成。

  1. 初始化:创建一个长度为m的位数组,所有位都初始化为0。
  2. 添加元素:当要向布隆过滤器中添加一个元素X时,将X分别通过k个独立的哈希函数计算出 k 个哈希值。每个哈希值对应位数组中的一个索引。然后,将这k个索引位置上的比特位都设置为1。
  3. 查询元素:当要查询一个元素Y是否可能在集合中时,同样将Y通过k个哈希函数计算出k个哈希值,得到k个索引。然后,检查位数组中这k个索引位置上的比特位。
    • 如果所有k个比特位都是1,则布隆过滤器判断Y可能在集合中。之所以是“可能”,是因为不同的元素经过哈希函数计算后,可能会得到相同的索引,导致“误判”(False Positive)。
    • 如果其中任何一个比特位是0,则布隆过滤器判断Y一定不在集合中。

布隆过滤器原理图

误判率(False Positive Rate)

布隆过滤器存在误判,即一个元素可能不在集合中,但被判断为在集合中。误判率与位数组的长度 m、哈希函数的个数k以及集合中元素的数量n有关。误判率p的计算公式为:

p = (1 - e^(-kn/m))^k

其中:

  • m 是位数组的长度(比特数)。
  • n 是集合中元素的数量(预期要插入的元素数量)。
  • k 是哈希函数的个数。

为了达到最小误判率,哈希函数的最佳个数 k 可以通过以下公式计算:

k = (m/n) * ln(2)

4.2、内存消耗计算

对于“40亿QQ号,1G内存”的问题,我们假设最多有n = 40亿个不重复的QQ号。我们需要在1GB内存限制下,尽可能降低误判率。

1GB内存 = 1 * 1024 * 1024 * 1024字节 = 8 * 1024 * 1024 * 1024 比特 = 8 * 2^30 比特。

所以,位数组的长度m = 8 * 2^30比特。

我们可以根据 nm 来计算在给定内存下的最小误判率 p 和最佳哈希函数个数 k

n = 4 * 10^9m = 8 * 10^9 (近似值,方便计算)

  1. 计算最佳哈希函数个数 kk = (m/n) * ln(2) = (8 * 10^9 / 4 * 10^9) * ln(2) = 2 * ln(2) ≈ 2 * 0.693 ≈ 1.386 由于 k 必须是整数,我们可以选择 k=1k=2。通常选择接近计算结果的整数。 如果选择 k=1,误判率会很高。 如果选择 k=2,误判率会降低。 我们取 k=2 进行计算。

  2. 计算误判率 pp = (1 - e^(-kn/m))^kp = (1 - e^(-2 * 4*10^9 / 8*10^9))^2p = (1 - e^(-1))^2p = (1 - 0.367879)^2p = (0.632121)^2 ≈ 0.3996

这意味着,在1GB内存(80亿比特)和40亿个QQ号的条件下,如果使用2个哈希函数,误判率将高达约 40%。这个误判率对于QQ号去重来说是不可接受的,因为这意味着有40%的概率会把一个新QQ号误判为已存在的QQ号,导致去重不准确。

如果我们需要更低的误判率,例如 p = 0.01% (0.0001),需要多少内存呢?

我们可以反过来计算 m

m = -n * ln(p) / (ln(2))^2

m = -4 * 10^9 * ln(0.0001) / (ln(2))^2ln(0.0001) ≈ -9.21ln(2) ≈ 0.693(ln(2))^2 ≈ 0.48

m ≈ -4 * 10^9 * (-9.21) / 0.48m ≈ 36.84 * 10^9 / 0.48m ≈ 76.75 * 10^9 比特

转换为GB: 76.75 * 10^9 比特 / 8 比特/字节 / (1024 * 1024 * 1024) 字节/GB ≈ 9.5 GB

这意味着,如果要求误判率达到0.01%,则需要大约 9.5 GB 的内存,这远超1GB的限制。

4.3、布隆过滤器的优缺点

优点:

  1. 空间效率极高:相比哈希表和位图,在允许一定误判率的情况下,布隆过滤器能以非常小的空间存储大量元素的存在信息。
  2. 时间效率高:插入和查询操作的时间复杂度都是O(k),其中k是哈希函数的个数,通常k很小,所以操作速度非常快。
  3. 保密性好:不存储元素本身,只存储元素的哈希映射,对隐私数据有一定的保护作用。

缺点:

  1. 存在误判率(False Positive):这是布隆过滤器最主要的缺点。它可能会把不存在的元素误判为存在。误判率随着插入元素的增多而增大,且无法避免。
  2. 不能删除元素:一旦一个比特位被设置为1,它可能被多个元素的哈哈希值共享。如果删除一个元素,将其对应的比特位设置为0,可能会影响到其他元素的判断,导致误判率增加或错误判断。
  3. 无法获取原始数据:布隆过滤器只能判断元素是否存在,无法获取元素本身。
  4. 对数据范围不敏感:与位图不同,布隆过滤器不需要知道元素的具体范围,只需要知道元素的数量 n
4.4、总结

布隆过滤器在内存受限且允许一定误判率的场景下非常有用,例如缓存穿透的预防、垃圾邮件过滤等。然而,对于“40亿QQ号去重”这个场景,如果要求精确去重(即0误判率),布隆过滤器并不适用,因为它无法保证100%的准确性。如果面试官允许一定的误判率,那么布隆过滤器可以作为一种备选方案,但需要根据可接受的误判率来计算所需的内存,通常会超出1GB的限制。

因此,在严格要求精确去重且内存极度受限的情况下,布隆过滤器不是最佳选择。

5、分治(Divide and Conquer)解决方案

当数据量过大无法一次性载入内存时,分治策略是处理海量数据问题的常用方法。其核心思想是将大问题分解为若干个小问题,分别解决,最后将小问题的结果合并,从而解决原问题。对于QQ号去重问题,分治策略主要有两种实现方式:外部排序和哈希分桶。

5.1、外部排序(External Sort)

原理:

外部排序适用于数据量远超内存容量的情况。

基本思想是:

  1. 分块读取与内存排序:将大文件中的QQ号数据分块读取到内存中。每块数据的大小应小于可用内存。在内存中对每块数据进行排序(例如使用快速排序),并去除块内的重复项。然后将排序并去重后的数据写入临时文件。
  2. 多路归并:当所有数据块都处理完毕并生成了多个有序的临时文件后,使用多路归并算法将这些临时文件合并成一个大的有序文件。在归并过程中,可以很容易地识别并去除跨文件块的重复项。

详细步骤:

  1. 分块与初步去重:

    • 从包含40亿QQ号的原始文件中,每次读取M个QQ号到内存中(M 的大小取决于可用内存,例如,如果1GB内存,每个QQ号4字节,可以读取1GB / 4字节 = 2.5亿个QQ号)。
    • 在内存中,使用std::unordered_setstd::sort + 遍历的方式对这M个QQ号进行去重和排序。由于内存限制,这里只能处理一部分数据,所以std::unordered_set可能仍然会内存溢出,更稳妥的方式是直接使用std::sort进行排序,然后遍历去重。
    • 将去重后的有序QQ号写入一个新的临时文件。重复此过程,直到原始文件中的所有QQ号都被处理完毕,生成多个有序的临时文件(例如 temp_0.txt, temp_1.txt, ..., temp_N.txt)。
  2. 多路归并与最终去重:

    • 打开所有临时文件,并为每个文件维护一个文件指针。每次从每个文件中读取一个QQ号,组成一个“当前值”集合。
    • 从“当前值”集合中找出最小的QQ号,将其写入最终结果文件。如果下一个最小的QQ号与当前写入的QQ号相同,则跳过(去重)。
    • 从该最小QQ号所属的临时文件中读取下一个QQ号,更新"当前值"集合,重复上述过程,直到所有临时文件都读取完毕。

外部排序去重原理图

内存消耗分析:

  • 分块阶段:每次只将一部分QQ号加载到内存中进行处理。内存消耗主要取决于每次加载的块大小。例如,每次加载2.5亿个QQ号,需要1GB内存。如果使用std::sort,则需要 2.5亿 * 4字节 = 1GB 内存。如果使用std::unordered_set,则需要 2.5亿 * (4+额外开销) 字节,可能仍然会超出1GB。
    • 优化:在分块阶段,如果内存仍然紧张,可以不进行内存内去重,只进行排序。将排序后的块写入临时文件。去重操作可以完全放到归并阶段进行。
  • 归并阶段:需要同时打开多个临时文件,并从每个文件中读取一个QQ号。内存消耗主要用于存储每个文件的当前QQ号(N_files * 4字节)和一个小型的优先队列(用于找出最小QQ号)。这部分内存消耗非常小,远低于1GB。

优缺点:

  • 优点:
    • 内存效率高:可以处理任意大的数据量,只要有足够的磁盘空间。
    • 精确去重:能够保证100%的去重准确性。
    • 结果有序:最终输出的QQ号是排序好的。
  • 缺点:
    • I/O开销大:需要多次读写磁盘,包括读取原始文件、写入临时文件、读取临时文件、写入结果文件,I/O操作是主要瓶颈。
    • 时间复杂度高:排序和归并过程的时间复杂度较高,通常为 O(N log N),其中N是总QQ号数量。
5.2、哈希分桶(Hash Bucketing)

原理:

哈希分桶是一种将大数据集分散到多个小数据集的方法,每个小数据集都可以独立处理。其核心思想是利用哈希函数将QQ号映射到不同的“桶”中,确保相同的QQ号一定会被映射到同一个桶。

详细步骤:

  1. 哈希分桶:

    • 遍历40亿个QQ号。对于每个QQ号 Q,计算其哈希值 hash(Q)
    • 根据哈希值 hash(Q) 将QQ号分配到 K 个不同的文件中(即“桶”)。例如,可以使用 hash(Q) % K 来决定写入哪个文件。这里的 K 需要根据可用内存和每个桶的预期大小来确定。
    • 关键在于,要保证 K 足够大,使得每个桶文件的大小都能够被加载到内存中进行处理。
  2. 桶内去重:

    • 逐个读取每个桶文件。对于每个桶文件,由于其大小已控制在内存范围内,可以将其所有QQ号加载到内存中。
    • 在内存中,使用std::unordered_setstd::sort + 遍历的方式对桶内的QQ号进行去重。
    • 将去重后的QQ号写入最终结果文件。

哈希分桶去重原理图

内存消耗分析:

  • 分桶阶段:内存消耗很小,每次只读取一个QQ号,计算哈希值,然后写入对应的文件。主要开销是打开和写入 K 个文件。
  • 桶内去重阶段:每个桶文件的大小必须小于可用内存。假设我们有1GB内存,每个QQ号4字节。那么每个桶文件最大可以存储 1GB / 4字节 = 2.5亿 个QQ号。如果原始数据有40亿个QQ号,那么至少需要 40亿 / 2.5亿 = 16 个桶。实际中,为了留出哈希表的额外开销,桶的数量会更多,例如32个或64个桶。

优缺点:

  • 优点:
    • 内存效率高:可以处理任意大的数据量,只要有足够的磁盘空间。
    • 精确去重:能够保证100%的去重准确性。
    • 可并行化:每个桶的去重过程是独立的,可以并行处理,提高效率。
  • 缺点:
    • I/O开销大:需要两次遍历原始数据(一次分桶,一次桶内去重),以及大量的磁盘读写操作。
    • 哈希函数选择:需要选择一个好的哈希函数,以确保QQ号均匀分布到各个桶中,避免数据倾斜。
5.3、总结

外部排序和哈希分桶都是处理海量数据去重的有效分治策略。它们都通过将大文件拆分成小文件,然后逐个处理小文件的方式来规避内存限制。

在实际应用中,哈希分桶通常更受欢迎,因为它更容易并行化,并且在某些情况下,如果哈希函数设计得当,可以避免全局排序的开销。

对于“40亿QQ号,1G内存”的问题,这两种方法都能提供精确的解决方案,但都需要权衡磁盘I/O和处理时间。

6、各种方案的优缺点总结与最终结论

经过对哈希表、红黑树、位图、布隆过滤器、外部排序和哈希分桶等多种去重方案的分析,我们可以对它们在“40亿QQ号,1G内存”这个特定场景下的适用性进行总结和比较。

6.1、方案对比总结
方案名称原理概述内存消耗(40亿QQ号)准确性优点缺点
哈希表存储所有不重复QQ号,O(1)查找~32 GB精确查找/插入效率高(平均O(1))内存消耗巨大,远超1GB限制
红黑树存储所有不重复QQ号,O(logN)查找~112 GB精确查找/插入效率高(O(logN)),数据有序内存消耗巨大,远超1GB限制
位图(Bitmap)每个QQ号对应1比特位,标记存在性0.5 GB精确内存效率极高,查找/插入O(1),结果有序适用于数据范围密集且不大,不适用于稀疏数据
布隆过滤器多个哈希函数映射到位数组,概率性判断1 GB (误判率~40%)概率性空间效率高,查找/插入O(k)存在误判率,不能删除,无法获取原始数据
外部排序分块内存排序,多路归并去重1 GB (分块内存)精确可处理任意大数据量,精确去重,结果有序I/O开销大,时间复杂度高(O(N log N))
哈希分桶哈希到多个小文件,桶内内存去重1 GB (桶内内存)精确可处理任意大数据量,精确去重,可并行化I/O开销大,需良好哈希函数,可能数据倾斜
6.2、最终结论与最佳实践

针对“40亿QQ号,1G内存,怎么去重?”这个面试题,我们需要在内存限制和去重准确性之间找到平衡。

  1. 如果QQ号是32位无符号整数,且要求100%精确去重:

    • 位图(Bitmap)是最佳方案。仅需0.5GB内存即可覆盖所有可能的32位QQ号,完美满足1GB内存限制,且去重结果精确,效率极高。这是最符合题目要求且性能最优的方案。
  2. 如果QQ号的范围非常大(例如64位整数),或者数据非常稀疏,导致位图内存超限,且要求100%精确去重:

    • 哈希分桶(Hash Bucketing)是最佳方案。这种方法通过将40亿个QQ号分散到多个小文件中,每个小文件的大小控制在1GB内存以内,然后对每个小文件进行内存去重。虽然会产生大量的磁盘I/O,但它能够保证去重的精确性,并且可以处理任意大的数据量。具体实现时,需要选择一个好的哈希函数,将QQ号均匀地分配到大约16-32个文件中(例如,hash(QQ号) % N_files),确保每个文件的大小在1GB以内,然后对每个文件单独使用std::unordered_set进行去重。
    • 外部排序也是一个可行方案,但通常在实际应用中,哈希分桶因其更好的并行化潜力而更受欢迎。
  3. 如果允许一定的误判率(例如,可以接受千分之一或万分之一的误判),且追求极致的内存效率:

    • 布隆过滤器可以考虑。但如前所述,在1GB内存下处理40亿个元素,即使是布隆过滤器,要达到较低的误判率(如0.01%),也需要远超1GB的内存(约9.5GB)。如果题目对误判率没有明确要求,或者允许较高的误判率(例如,40%),那么布隆过滤器在1GB内存下可以工作,但其结果可能不符合“去重”的实际需求。

总结

面试官提出这个问题,通常是希望考察你对位图、布隆过滤器、哈希分桶等大数据处理技术的理解和应用。

最直接且高效的答案是位图,因为它在32位QQ号的假设下,完美契合了内存和精确去重的要求。

如果面试官追问QQ号范围更大或更稀疏的情况,则应转向哈希分桶外部排序

在回答时,清晰地阐述每种方案的原理、内存计算、优缺点,并根据题目给出的约束条件(特别是QQ号的范围和内存限制)进行选择和解释,将能充分展现你的技术深度和解决问题的能力。