用位运算 + 按位统计来求「数组中只有一个数字出现一次,其它数字都出现三次」的问题
默认是处理非负数(positive number)
解释逻辑:
- 数组里除了一个数只出现一次,其余数字都出现三次
- 对每个二进制位统计 1 出现的总次数
- 最后对 3 取模(
% 3
)只剩下出现一次的那个数在该位上的贡献
int[] nums = {2, 2, 3, 2}; 含义:
- 数组里:数字
2
出现了三次 - 数字
3
出现了一次
题目保证:除了一个数只出现一次,其余数都出现三次
为什么一定要出现恰好 k 次?
因为:
- 取模的原理依赖于「其它数的贡献都是 k 的倍数」
- 如果其它数出现了非 k 次,比如 2 次、4 次,就不能保证「其它数贡献的 1 总数 % k == 0」
(num >> (31 - i)) & 1
二进制右移找1
解释:
num >> (31 - i)
:把 num 的二进制右移到第 i 位移到最低位& 1
:只看最低位是1
还是0
得到结果是:
- 第 i 位是 1:加 1
- 第 i 位是 0:加 0
下面是 i 从 0 到 3 时的流程示意:
i | 31 - i | num >> (31-i) | 得到的最低位 | bitSums[i] += |
---|---|---|---|---|
0 | 31 | 000...00000000 | 最低位是 0 | 加 0 |
1 | 30 | 000...00000000 | 最低位是 0 | 加 0 |
... | ... | ... | ... | ... |
30 | 1 | 000...00000001 | 最低位是 1 | 加 1 |
31 | 0 | 000...00000011 | 最低位是 1 | 加 1 |
统计 bitSums(每个位上 1 出现的次数)
bitSums 数组有 32 个元素,但只看最后两位(i=30 和 i=31)
i | (31 - i) | 表示第几位 | 每个数贡献 | bitSums[i] |
---|---|---|---|---|
30 | 1 | 倒数第2位 | 2:1,2:1,3:1,2:1 → 共4个1 | 4 |
31 | 0 | 最低位 | 2:0,2:0,3:1,2:0 → 共1个1 | 1 |
+ bitSums[i] % 3
- 统计的次数对 3 取模,只会是 0 或 1
- 如果是 1:说明这一位只出现一次的数字在该位是 1
- 如果是 0:说明该位只出现一次的数字是 0
public class A_SingleNumber {
public int singleNumber (int [] nums) {//输入:一个整型数组 `nums`
int [] bitSums = new int[32];//创建了一个长度为 32 的数组
for (int num : nums) {
for (int i = 0; i < 32; i++) {
bitSums[i] += (num >> (31-i)) & 1;
}
}
int result = 0;
for (int i = 0; i < 32; i++) {
result = (result << 1) + bitSums[i] % 3;//当前结果左移一位,准备在最低位加上当前位
}
return result;//返回计算出的只出现一次的数字
}
public static void main(String[] args) {
A_SingleNumber obj = new A_SingleNumber();
int[] nums = {2, 2, 3, 2};
int ans = obj.singleNumber(nums);
System.out.println("Single number is: " + ans);
}
}
i=0 到 i=29:
bitSums[i]=0 → bitSums[i]%3=0
每次:
- result<<1 → result 还是 0
- +0 → result 还是 0
所以 result 还是 0
i=30:
- bitSums[30]=4
- 4%3=1 → 当前位是1
- result=0
- result<<1=0
- +1 → result=1
i=31:
- bitSums[31]=1
- 1%3=1 → 当前位是1
- result=1
- result<<1=2
- +1 → result=3
最后得到:
- result=3
说明数组里只出现一次的数字是:3
唯独只出现一次的数字,它的1在那一位上会多出来
必须规定:
- 除了一个数只出现一次
- 其它数都出现恰好 k 次(比如 3 次)
才能通过「按位统计 1 次数,对 k 取模」来找出只出现一次的数
补充:从左到右四位:
i | bitSums[i]%3 | result计算过程 |
---|---|---|
0 | 1 | result=0<<1+1=1 |
1 | 0 | result=1<<1+0=2 |
2 | 1 | result=2<<1+1=5 |
3 | 1 | result=5<<1+1=11 |
4%3=1
4 ÷ 3 = 1 ...... 1
1%3=1
1 ÷ 3 = 0 ...... 1