思路
首先,我们现了解^异或这个位运算符,它有以下的性质:
- 一个数和自己异或结果为0
- 任何数和0异或结果都是本身
不懂的百度
因为这个数组是包含两个不同的数假设为a和b,还有若干个出现两次的数。所以我们通过^运算,可以将若干个出现两次的数都排除只留下a和b的异或值s。
for(int i:nums)
s^=i;//这个s最后就是a^b的结果
现在我们拿到了a和b的异或值,下一步就是如何将a和b分解出来。我们知道因为a!=b,所以s的二进制肯定有一位为1,且这一位能够区别出a和b,换句话说就是若a的该位置为1,那么b的该位置就为0。
int a=3,b=5,s=6;//假设现在这两个不同的数,他们的异或为6
3 00000101
5 00000011
^
6 00000110
//那么首先我们先找出能够区别a和b的那一位1
int k=s&(-s) //这个操作能够获取s的为1的最低位的那个数
s 6 00000110
-s -6 10000110 原码
-s -6 11111001 反码
-s -6 11111010 补码 //这里三步求的是-s的二进制表示,负数的二进制就是它的补码
然后将s&(-s)
s 00000110
-s 11111010
k 00000010 这个k就是s各个位中最小的1位。
这样我们拿到这个k之后就可以分辨出a和b了。
我们将k和每一个num进行&操作进行分组,若为0,则表示这个num的那一位为1这一组,否则则表示这个num的那一位为0的那一组,因为a和b的那一位是不同的,则a和b肯定会被分到不同的组中。然后我们再每组中进行^操作去除出现两次的值,就分别得到a和b了。
int k=s&(-s);
int re[]=new int[2]; //假设a的那一位为1,b的为0
for(int i:nums){
if((k&i)==0){//这一组表示那一位为1的数,包含a
re[0]^=i;//然后这一组肯定是有若干出现两次的数和a组成,可以通过^取出a
}
else{//这一组表示那一位为0的数,包含b
re[1]^=i;
}
}
代码
class Solution {
public int[] singleNumbers(int[] nums) {
int s=0;
for(int i:nums)
s^=i;//最后这个s就是那两个不同的数a和b的异或值
int k=s&(-s);//这个k就是a或b中最低的1的值
int[] re=new int[2];
for(int i:nums){
if((i&k)!=0) re[0]^=i;
else re[1]^=i;
}
return re;
}
}