【LeetCode】137. 只出现一次的数字 II
解题思路
问题要求在整数数组中找到唯一出现一次的元素(其他元素均出现三次),需满足 O(n) 时间和 O(1) 空间复杂度。两种主流解法:
- 位状态机(高效解法):
用one
和two
两个变量分别记录当前位出现 1 次和 2 次的状态。当数字第三次出现时,通过位运算重置状态,最终one
即为结果。 - 逐位统计(直观解法):
统计每位 1 出现的总次数,对 3 取余。若余数非零,说明目标数字在该位为 1,据此逐位构建结果。
关键步骤
解法一:位状态机(高效)
- 初始化:
one = 0
,two = 0
,分别记录出现 1 次和 2 次的位。 - 遍历数组:
two
更新:two |= one & num
→ 将one
中已存在的位同步到two
(标记出现两次)。one
更新:one ^= num
→ 记录出现奇数次的位(首次或第三次出现时翻转)。- 计算
three = one & two
→ 标记出现三次的位。 - 重置:
one &= ^three
,two &= ^three
→ 清除出现三次的位。
- 返回结果:
one
即为目标数字。
解法二:逐位统计(通用)
- 遍历 32 位(整数位数)。
- 统计当前位 1 的总数:
- 对每个数右移
i
位后与1
相与,累加结果到cnt
。
- 对每个数右移
- 判断目标位:
- 若
cnt % 3 != 0
,说明目标在该位为 1,设置ans
的对应位。
- 若
- 组合结果:通过位或操作
ans |= (1 << i)
构建目标数字。
代码实现
解法一:位状态机
解法二:逐位统计
示例测试
复杂度分析
解法 | 时间复杂度 | 空间复杂度 |
---|---|---|
位状态机 | O(n) | O(1) |
逐位统计 | O(32n) | O(1) |
- 位状态机:仅需单次遍历,效率更高。
- 逐位统计:固定 32 次内循环,仍满足线性要求,但常数较大。
关键点总结
- 位状态机优势:
- 单次遍历:无需内循环,效率更优。
- 状态转移:通过
one
和two
的位运算实现状态机,清除三次出现的位是核心。
- 逐位统计优势:
- 直观易扩展:可推广至其他重复次数(如 5 次、7 次)。
- 负数处理:需额外处理符号位(如用
int32
循环),但本题未涉及。
- 选择建议:
- 追求效率 → 位状态机。
- 追求可读性/扩展性 → 逐位统计。
- 注意事项:
- 两种方法均满足 O(1) 空间,仅用常数变量。
- 逐位统计中,
cnt % 3
等价于cnt % 3 == 1
(目标数字贡献 1 次)。