1. 程式人生 > >用異或實現查詢只出現一次的數字

用異或實現查詢只出現一次的數字

C語言中我對按位異或這種運算不太懂,通過這道題熟悉了一些相關的運算以及基本性質。

題目如下:

一個數組中只有兩個數字是出現一次,其他所有數字都出現了兩次,請找出這兩個數字。

首先對於這樣一道題我是具體化分析比如這個陣列就是{1,3,5,7,1,3,5,9},題目要求即找出只出現一次的數字即7和9。

之前在異或那邊看過這樣一道題,也算是這題目的簡單版本,一個數組裡面只有一個數字出現一次,其他都出現兩次請找出這個數字。如何解決這樣一道題?我之前想法就是用迴圈多次遍歷,選出一個數比如叫a,再在剩下的數字裡面遍歷看是否有a相等的數字。這個比較繁瑣,我們可以考慮用異或的性質解決,一個數字異或它自己結果為0,異或0結果為它自己即a^a=0,a^0=a,且異或滿足a^b^c=a^(b^c)。

因此我們可以設定一個ret異或每個陣列元素,最後相同的都抵消為0,那個唯一的數字異或0為它自己即為答案。

程式碼如下:

int find_num_once(int a[], int n)
{
	int i = 0;
	int ret = 0;
	for (i = 0; i < n; i++) 
		ret ^= a[i];
	return ret;
}
有了上面的基礎,我們不妨可以把原陣列分為兩個子陣列。在每個子陣列中,包含一個只出現一次的數字,而其它數字都出現兩次。如果能夠這樣拆分原陣列,按照前面的辦法就是分別求出這兩個只出現一次的數字了。

我們還是設定一個ret異或每個陣列元素,由於其它數字都出現了兩次,在異或中全部抵消為0,

那麼最終得到的結果就是兩個只出現一次的數字的異或結果ret。由於這兩個數字不一樣,那麼這個異或結果ret肯定不為0 ,即這個結果數字的二進位制表示中至少就有一位為1 。我們在結果數字中找到第一個為1 的位的位置,記為第pos 位。我們以第pos 位是不是1 為標準把原陣列中的數字分成兩個子陣列,第一個子陣列中每個數字的第pos 位都為1 ,而第二個子陣列的每個數字的第pos位都為0 。最後將子陣列按照之前方法就可以分別得到各自的唯一數字。

程式碼如下:

#include <stdio.h> 

int main()
{
	int a[] = {1,3,5,7,1,3,5,8};
	int sz = sizeof(a) / sizeof(a[0]);
	int num1 = 0, num2 = 0;
	find_num(a, sz, &num1, &num2);
	printf("%d %d\n", num1, num2);
	return 0;
}

int find_num(int a[], int n, int *pNum1, int *pNum2)
{
	int i = 0;
	int ret = 0;
	for (i = 0; i < n; i++) 
		ret ^= a[i];//ret為兩個不同數字異或結果 
		
	int pos = 0;
	while (((ret >> pos) & 1) != 1) 
		pos++;
	
	for (i = 0; i < n; i++) {
		if ((a[i] >> pos) & 1== 1)//分組條件
			*pNum1 ^= a[i];
//		else
//			*pNum2 ^= a[i];
	}
	*pNum2 = ret ^ *pNum1;//ret = *pNum1 ^ *pNum2所以*pNum2 = (*pNum1 ^ *pNum2) ^ *pNum1
}