一、运行结果
二、题目
三、思路
异或运算:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
n^0 = n;
n^n = 0;
nnm = n(nm) 满足结合律
1.我们可以让数组中的每一个数异或一下,最后会得到一个结果retall,
2.就是两个出现一次的数字的异或结果这个结果肯定是由两个不同数字异或而来,因此我们找retall二进制中为1的位置i,因为1一定是由0,1异或而来,因此要求得两个数中,一定有一个数的二进制中的第i个位置为1, 一个为0.
3.对和这个数相与为1的求异或是第一个数,为0的是第二个数
之所以让retall与其相反数相与是因为互为相反数的两个数只有一位二进制位是同时为1(并且不是符号位),而其他位相与之后必定为0(包括符号位),这里只是为了确保只选出retall中的一个为1的位,具体是哪一位不用管。对于上面的第三点做补充:出现两次的数第i位上是1或0并不影响,因为还要做异或运算,如果是出现两次的数,异或后必然为0,相与为1或为0只是为了将只出现一次的两个数分开在两个不同的子集里。
大致思路参考:https://blog.csdn.net/Fizz6018/article/details/107201709/
四、代码
class Solution {
public:
vector<int> FindNumsAppearOnce(vector<int>& array) {
vector<int> ans;
int len = array.size();
int retall = 0; //所有的数异或的结果
for(const int val : array){
retall ^= val; //最终得到的是不同的两个数异或的结果,出现两次的异或后都变为0
}
retall &= (-retall); //两个相反数补码必然只有一位是同时为1的(且非符号位)
int tmp1 = 0, tmp2 = 0;
for(const int val : array){
if(retall & val){ //与retall相与为1得到其中一个数
tmp1 ^= val;
}
else{ //得到另一个数
tmp2 ^= val;
}
}
ans.push_back(min(tmp1, tmp2));
ans.push_back(max(tmp1, tmp2));
return ans;
}
};