1亿个正整数,范围是0-42亿。求出现次数是2的数字,空间复杂度

73 阅读2分钟

问题分析

数据规模

• 共有 1 亿(10⁸)个正整数

• 数值范围为 0 到 42 亿(4.2 × 10⁹)

目标

• 找出所有 出现次数恰好为 2 的数字。

空间复杂度

• 需要优化空间使用,不能直接存储所有数据。

解法 1:位图(BitSet)

思路

  1. 由于数据范围是 0 ~ 42 亿(4.2 × 10⁹) ,可以用 2-bit 位图(每个数字占 2 位)来记录 出现次数

• 00:未出现

• 01:出现 1 次

• 10:出现 2 次

• 11:忽略(因为只统计出现 2 次的数)

  1. 需要 2-bit × 42 亿 ≈ 1GB 内存,符合可行范围。

  2. 步骤

第一遍遍历:统计所有数字的出现次数(使用 2-bit 位图)。

第二遍遍历:筛选出 出现 2 次的数 并输出。

实现方式

import numpy as np

def find_numbers_appearing_twice(nums):
    N = 4200000000  # 42亿
    bits_per_number = 2
    total_bits = N * bits_per_number
    bit_array_size = total_bits // 8  # 转换成字节

    # 使用 NumPy 创建一个位数组,节省空间
    bit_array = np.zeros(bit_array_size, dtype=np.uint8)

    def get_bits(num):
        """获取 num 在 bit_array 里的 2-bit 值"""
        byte_index = (num * 2) // 8
        bit_offset = (num * 2) % 8
        return (bit_array[byte_index] >> bit_offset) & 0b11

    def set_bits(num, value):
        """设置 num 在 bit_array 里的 2-bit 值"""
        byte_index = (num * 2) // 8
        bit_offset = (num * 2) % 8
        bit_array[byte_index] &= ~(0b11 << bit_offset)  # 先清空对应的 2-bit
        bit_array[byte_index] |= (value << bit_offset)  # 赋值

    # 1. 统计数字出现次数
    for num in nums:
        count = get_bits(num)
        if count < 2:  # 只记录 0->1 和 1->2
            set_bits(num, count + 1)

    # 2. 找出出现 2 次的数
    result = []
    for num in range(N):
        if get_bits(num) == 2:
            result.append(num)

    return result

时间复杂度

O(n) :遍历两次数据列表。

空间复杂度

• 2-bit × 42 亿 ≈ 1GB

适用场景

适用于大规模数据,使用 位图减少空间

解法 2:哈希表

思路

• 使用 字典(HashMap) 统计出现次数。

• 但 存储 1 亿个数的哈希表会消耗大量空间,不适合此题。

空间复杂度

• Python dict 需要存储 键 + 计数,大约需要 3~4GB 内存,过大。

结论

哈希表不可行,需要用 位图优化

解法 3: 排序 + 线性扫描

思路

  1. 先排序(O(n log n))。

  2. 遍历数组,找出恰好出现 2 次的数(O(n))。

时间复杂度

• 排序 O(n log n)

• 遍历 O(n)

总时间:O(n log n)

空间复杂度

• 需要 存储排序后的数组(1 亿个数 400MB),比哈希表节省,但仍比位图高。

适用场景

如果数据可以排序(外部排序),可用此方法

最终选择

位图(BitSet) 最优O(n) 时间,O(1GB) 空间)。

哈希表不可行(占用空间太大)。

排序可行,但比 位图 慢(O(n log n))。