分组异或
题目
数组中数字出现的次数
题意
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)
分析
如果是数值中只有一个这样的数字,我们应该怎么做?
根据位运算我们可以知道,a ^ a = 0, 0 ^ a = a, a ^ b ^ c = a ^ c ^ b
我们只要将数组遍历异或一遍最后的异或值就是这样的一个数字
此题目中有两个这样的数字a, b,因此我们按上述操作得到的结果是这两个数字的异或值,不是我们需要的答案
因此我们需要将数组分为两个数组,满足下列的要求
- 相同的数字要分到同一个数组中
- a, b要分到不同的数组中
只要满足上述的两个条件,就能分组异或,分别得到a, b
要将a, b分至不同的数组中,就要找到他们的不同点加以区分,有上述我们知道,遍历异或的到的是a, b的异或值
由异或的计算过程可知,只有在其a, b的二进制位不相等的位置才能二进制位是1,因此我们只要找到a ^ b的值的二进制位上为1的位,这样就满足了条件2
因为相同的数在其对应的二进制位上的值是相同的所以必然会被分到一组,满足条件1
算法
先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
在异或结果中找到任意为 1 的位(其他位为0),这里使用不为0的最低位(利用lowbit做)。
根据这一位对所有的数字进行分组。
在每个组内进行异或操作,得到两个数字。
class Solution {
public int[] singleNumbers(int[] nums) {
int[] res = new int[2];
int ans = 0;
for(int item : nums) ans ^= item;
int lowbit = ans & (-ans);
for(int item : nums){
if((lowbit & item) == 0)
res[0] ^= item;
}
res[1] = ans ^ res[0];//自反性
return res;
}
}