陣列中只出現一次的數字,時間複雜度O(n),空間複雜度O(1)的解法
題目:一個整型數組裡除了兩個陣列外,其他的數字都出現了兩次,要找出這兩個數字。
異或運算有一個性質:任何數異或它自己,結果都是0;這樣如果題目變成只有一個數字只出現一次,其他數字均出現兩次,這樣我們從頭到尾異或陣列中的每一個數字,那麼最終的結果就是隻出現一次的數字。
如果可以把陣列中的數字分成兩個子陣列,每個數組裡麵包含一個只出現一次的數字,其他的數字都出現兩次,這樣按照上面的方法就可以分別求出只出現一次的數字了。首先,我們從頭到尾異或陣列中的每一個數字,那麼得到的結果將是這兩個只出現了一次的數字異或後的結果(因為其他出現了兩次的數字都在異或中抵消了)。由於這兩個數字不相同,所以它們的異或結果的二進位制表示中至少有一位是1.我們先找到第一個為1的位的位置,記為第n位。然後我們以第n位是否為1,把原陣列分成兩個子陣列,第一個陣列中每個數的第n位都為1,第二個陣列中的每個數的第n位都為0,因為兩個相同的數字的任意一位都是相同的,所以相同的數肯定分在同一組,這樣我們就實現了劃分子陣列的效果。
參考程式碼如下:
#include<iostream>
#include<cstdio>
using namespace std;
bool Is1(int n,unsigned int bit)
{
n=n>>bit;
return (n&1);
}
unsigned int FindFirstBit1(int n)
{
int bit=0;
while((n&1)==0&&bit<8*sizeof(int))
{
n=n>>1;
bit++;
}
return bit;
}
void FindNums(int data[],int len,int &num1,int &num2)
{
if(data==NULL||len<2)
return;
int ans=0;
for(int i=0;i<len;i++)
ans=ans^data[i];
unsigned int bit=FindFirstBit1(ans);
for(int i=0;i<len;i++)
{
if(Is1(data[i],bit))
num1=num1^data[i];
else
num2=num2^data[i];
}
}
int main()
{
int num1=0,num2=0;
int data[100];
int len;
scanf("%d",&len);
for(int i=0;i<len;i++)
scanf("%d",&data[i]);
FindNums(data,len,num1,num2);
printf("%d %d",num1,num2);
}