问题分析
• 数据规模:
• 共有 1 亿(10⁸)个正整数。
• 数值范围为 0 到 42 亿(4.2 × 10⁹) 。
• 目标:
• 找出所有 出现次数恰好为 2 的数字。
• 空间复杂度:
• 需要优化空间使用,不能直接存储所有数据。
解法 1:位图(BitSet)
思路:
- 由于数据范围是 0 ~ 42 亿(4.2 × 10⁹) ,可以用 2-bit 位图(每个数字占 2 位)来记录 出现次数:
• 00:未出现
• 01:出现 1 次
• 10:出现 2 次
• 11:忽略(因为只统计出现 2 次的数)
-
需要 2-bit × 42 亿 ≈ 1GB 内存,符合可行范围。
-
步骤:
• 第一遍遍历:统计所有数字的出现次数(使用 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: 排序 + 线性扫描
思路:
-
先排序(O(n log n))。
-
遍历数组,找出恰好出现 2 次的数(O(n))。
时间复杂度:
• 排序 O(n log n)
• 遍历 O(n)
• 总时间:O(n log n)
空间复杂度:
• 需要 存储排序后的数组(1 亿个数 400MB),比哈希表节省,但仍比位图高。
适用场景:
• 如果数据可以排序(外部排序),可用此方法。
最终选择
• 位图(BitSet) 最优(O(n) 时间,O(1GB) 空间)。
• 哈希表不可行(占用空间太大)。
• 排序可行,但比 位图 慢(O(n log n))。