《劍指offer》:[40]陣列中只出現一次的數字

塵虛緣_KY發表於2016-06-19
題目:一個整型陣列裡除了兩個數字外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。要求時間複雜度為O(N),空間複雜度為O(1)。
例如輸入陣列{2,4,3,6,3,2,5,5},因為只有4和6在這個陣列裡出現了一次,所以最後輸出的是4,6。
      分析:因為題目要求的時間複雜度和空間複雜度分別為:O(N)和O(1)。所以這個題目不能借助輔助空間,那麼也就是要在一次遍歷後就能找出只出現一次的資料還是有一定的難度的。我在第一次看這個題目的時候也沒想到什麼好的辦法。最後看了書上的提示才想到這個解決方案。
       首先我們考慮只有一個數字出現的情況,這時候我們該怎麼做呢?也就是其他的數字都是成對的出現,而唯獨只有一個數字是單獨出現。注意這句話非常重要。成對出現的數字有什麼特點嗎?這裡有點難想到,就是這個切入點很難想到,那就是異或運算。相同的數字經過異或運算後,就變為了0。然後如果一個數字和0異或運算就是它本身。說到這兒,應該就明白了吧!我們將陣列裡的每個數字進行異或運算,如果只有一個數字最後出現過一次,那麼經過異或完以後,最後剩下的那個不為0的數字就是我們要找的那個數字。因為成對出現的都消失了,剩下的就是孤苦無依的那個單的,呵呵!
     看完了只有一個數字出現一次的情況,我們再來看看有兩個數字出現的情況。想想成對的經過異或運算都化為了0.那麼剩下了兩個資料怎麼樣了呢?一定是不為0,那麼我們可以尋找這兩個數字的不同,哪裡不同?異或運算不為0就是不同啊,所以我們將數字同過異或完最後結果的倒數第一位不是0的二進位制位來將陣列分為兩組,這樣的話就把我們需要找的兩個數字分到了兩個陣列裡,並且在這個兩個子陣列裡,除了一個數字出現一次外,其他的都是成對的出現,這下好了,問題轉化為我們討論的第一種情況,那麼困難也就迎刃而解了!
具體實現程式碼:
#include <iostream>
using namespace std;
int arr[8]={2,4,3,6,3,2,5,5};
//判斷二進位制位是否是1;
bool IsBit1(int num,unsigned int indexBit)
{
	num=num>>indexBit;
	return num&1;
}
//找到陣列進行異或操作後第一個是1的二進位制位。
unsigned int FindFirstBitIs1(int num)
{
	int indexBit=0;
	while((num & 1)==0 && (indexBit<8*sizeof(int)))
	{
		num=num>>1;
		++indexBit;
	}
	return indexBit;
}
//找到出現一次的的數子
void FindNumAppearOnce(int *arr,int length,int *num1,int *num2)
{
	if(arr==NULL || length<2)
		return ;
	int resultExclusiveOR=0;
	for(int i=0;i<length;++i)
		resultExclusiveOR^=arr[i];
	unsigned int indexof1=FindFirstBitIs1(resultExclusiveOR);
	*num1=*num2=0;
	for(int j=0;j<length;++j)
	{
		if(IsBit1(arr[j],indexof1))
			*num1^=arr[j];
		else
			*num2^=arr[j];
	}
}
int main()
{
	int num1,num2;
	FindNumAppearOnce(arr,8,&num1,&num2);
	cout<<"只出現一次的兩個數為:"<<num1<<" "<<num2<<endl;
	system("pause");
	return 0;
}

執行結果:


相關文章