- 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
下面的思路来自《剑指offer》。
-
将问题转化为只有一个数字只出现了一次。首先考虑数组中只有一个数字只出现了一次,只需要将数组中每个数字进行异或,最后得到的数字就是那个只出现了一次的数字,因此其他数字都是出现两次的,在异或的过程中被消掉了。
然后问题就变成如何将数组拆分成两组(不一定要把两组数字拷贝到新的数组中),每组中都只有一个数字只出现一次。仍然可以通过异或的方式进行分组,将整个数组异或一遍,最后得到的结果肯定不是
0
,因为有两个数字是不一样的,而且这个结果就是这两个只出现一次的数字异或的结果,因此可以找到异或结果中一个为1
的位,可以就取从右向左第一个为1
的位,假如是第n
位,这两个数字在这一位上肯定是不一样的,而其他数字在这一位上一定是两两相同的,因此可以以这一位为依据,这一位为1
的所有数字分为一组,这一位为0
的数字分为另一组,再求出这两组中只出现一次的数字就可以解决问题了。在第二次遍历的时候可以通过判断这个数字的第
n
为是否是1
来判断这个数字属于哪一组,这样就不用把两组数字分别拷贝出来了,整个过程中就只需要对整个数组遍历两次即可。因此整个过程时间复杂度为$O(n)$,空间复杂度为$O(1)$。 -
注意:有
&
与运算的式子要加小括号,不然运算顺序可能会有问题,因为==
的优先级比&
更高,比如num&1==0
就会先算后面的。
- C++
class Solution {
private:
int findFirstBit1(int num){
int bitIndex = 0;
int bitInt = 8*sizeof(int);
// 注意&的式子要加小括号,因为==优先级更高
while(((num&1)==0) && bitIndex<bitInt){
bitIndex++;
num = num >> 1;
}
return bitIndex;
}
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int length = data.size();
if(length<2) return;
if(length==2){
*num1 = data[0];
*num2 = data[1];
return;
}
int XorResult = data[0];
for(int i=1; i<length; i++) XorResult ^= data[i];
int flag = 1 << findFirstBit1(XorResult);
*num1 = *num2 = 0;
for(int i=0; i<length; i++){
// 注意&的式子要加小括号,因为==优先级更高
if((flag&data[i])==0) *num1 ^= data[i];
else *num2 ^= data[i];
}
}
};