1 題目及要求
1.1 題目描述
一個整數數組中除了兩個數字只出現一次之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。
例如輸入數組{2, 4, 3, 6, 3, 2,5, 5}只有4和6只出現了一次,其他的都出現了兩次,所以輸出是4和6。
2 解答
2.1 題目分析
只有一個數字只出現一次時,用異或運算的性質:任何數和本身的異或得0。也就是說從頭到尾依次異或數組中的每個數字,那麼最終的結果剛好是那個只出現一次的數字,因爲那些成對出現兩次的數字全部在異或中抵消了。
因此對於兩個出現一次的數字,我們試着把原數組分成兩個子數組,使得每個數組只包含一個只出現一次的數字,而其他的數字都是成對地出現兩次。
首先,我們還是從頭到尾依次異或數組中的每個數字,那麼最終結果就是兩個只出現一次的數字的異或的結果。因爲其他數字都出現了兩次,在異或中全部抵消了。由於這兩個數字肯定不一樣,那麼異或的結果肯定不是0,也就是說起二進制表示中至少有一位是1。我們在結果中找到某一位爲1的位置(代碼實現的是最低位的1),記爲第bit位。然後我們以第bit位是不是1把原數組的數字分成兩組,第一組中的數字的bit位都爲1,第二組的爲0。那麼兩個相同的數字一定被分到同一組中,而兩個只出現一次的數字一定被分到不同的組中。這樣每個子數組都只有一個數字只出現一次,其他的都出現兩次。
2.2 代碼
#include <iostream>
using namespace std;
unsigned findFirstBit1(int n){
unsigned bit = 0;
for(bit=1;bit<sizeof(int)<<3 && !(n&1<<bit);++bit);
return bit;
}
void findTwoNumbersAppearOnce(int *v, int n, int *num){
if(!v || !num || n<2) return;
int resXor = 0;
for(int k1(0);k1<n;++k1) resXor ^= v[k1];
int difBit = 1<<findFirstBit1(resXor);
// int difBit = rexXor & (~resXor+1); // 該句和更加簡潔
num[0] = num[1] = 0;
for(int k1(0);k1<n;++k1)
if(v[k1]&difBit) num[0] ^= v[k1];
else num[1] ^= v[k1];
}
int main(){
int v[] = {2,4,3,6,3,2,5,5};
int res[2];
findTwoNumbersAppearOnce(v,8,res);
cout << res[0] << ' ' << res[1] << endl;
return 0;
}