287. 寻找重复数:位运算解法
题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 [1, n] 范围内。
假设 nums 只有一个重复的整数,请找出这个重复的数。
要求:
- 不能修改数组
nums。 - 只使用常数级额外空间。
示例:
1. 题型判断
除了快慢指针,本题还可以使用位运算。
核心思路是:既然数字都在 [1, n],那么正常情况下,数组应该包含 1 到 n 这些数各一次。但现在 nums 的长度是 n + 1,多出来的那部分一定来自重复数字。
我们可以逐个二进制位比较:
nums中这一位为1的数量。[1, n]中这一位为1的数量。
如果某一位上,nums 中 1 的数量更多,说明重复数字在这一位上是 1。
把这些多出来的二进制位拼起来,就是重复数字。
2. 位含义
使用二进制位来还原重复数字。
bit:当前检查的二进制位。numsCount:nums中第bit位为1的数字数量。rangeCount:1到n中第bit位为1的数字数量。ans:最终还原出的重复数字。
判断某个数字 num 的第 bit 位是否为 1:
如果结果是 1,说明这一位存在。
3. 维护规则
设 n = nums.length - 1。
从低位到高位枚举每一个二进制位:
- 遍历
nums,统计当前位为1的个数,得到numsCount。 - 遍历
[1, n],统计当前位为1的个数,得到rangeCount。 - 如果
numsCount > rangeCount,说明重复数字当前位是1,把这一位加入答案。
加入答案的方式:
4. 答案更新时机
每一位统计完成后,都可以判断这一位是否属于重复数字。
如果:
说明重复数字在当前位上贡献了额外的 1,于是更新:
所有二进制位处理完成后,ans 就是重复数字。
5. 边界条件与易错点
边界条件:
nums.length = n + 1,所以n = nums.length - 1。- 数字范围是
[1, n],只需要统计到n的最高二进制位。 - 题目保证存在重复数,因此最后一定能还原出答案。
易错点:
- 统计
[1, n]时不能从0开始,因为题目数字范围不包含0。 - 判断当前位时要写成
((num >> bit) & 1) === 1,避免把位运算结果和布尔值混在一起。 - 只有当
numsCount > rangeCount时,当前位才应该加入答案。 - 位运算不会修改原数组,满足题目要求。
6. 代码实现
7. 复杂度分析
- 时间复杂度:
O(n log n)。需要枚举n的每一位,位数约为log n,每一位都遍历nums和[1, n]。 - 空间复杂度:
O(1)。只使用了计数变量和答案变量,没有修改原数组。
为什么这个方法成立
以 nums = [1,3,4,2,2] 为例,n = 4,正常范围是 [1,2,3,4]。
看二进制:
nums 比 [1,4] 多出来的数字是一个额外的 2。
逐位统计:
第 0 位,也就是最右边这一位:
实际数组没有比正常范围多,所以重复数字第 0 位不是 1。
第 1 位:
实际数组比正常范围多了一个 1,所以重复数字第 1 位是 1。
第 2 位:
实际数组没有比正常范围多,所以重复数字第 2 位不是 1。
最后得到:
拼起来是:
也就是十进制的 2。
统计所有位之后,就能把重复数字 2 的二进制形态 010 还原出来。

