一個百度的面試題目

問題:給一個很大的數組,裏面有兩個數只出現過一次,其他數都出現過兩次,把這兩個數找出來

 

原理:兩個相同的數進行異或,其結果爲0,兩個不相同的數急性異或,其結果不爲0。

舉例:99 ^ 99 = 1100011b ^ 1100011b = 0

      95 ^ 99 = 1011111b ^ 1100011b = 0111100b= 60

 

在題目給出的很大的數組中,除要找的兩個不同的數字外,其它的數字都是成對出現的,根據上面說到的兩個相同的數字的異或其結果爲0,因此,如果將整個數組中的元素進行異或,所得到的結果應該是所求的那兩個不成對的數字的異或結果。假定數組中兩個不同的數字是95 ^ 99,其異或的二進制結果是0111100b,其中有4位是1,這表明這兩個數字的二進制有4位是不同的。(從右往左數) 它們分別是第3、4、5、6這4位,於是我們

只需要將數組中所有元素中第6位爲1的元素和0111100b異或,

或者,將數組中所有元素中第5位爲1的元素和0111100b異或,

或者,將數組中所有元素中第4位爲1的元素和0111100b異或,

或者,將數組中所有元素中第3位爲1的元素和0111100b異或。

就可以得到所求之兩個數字中的一個數字,不妨以上面最後一條規則爲例來進行說明:數組元素中第3位爲1的數字,除所求的兩個數字之外,都是成對出現的,它們所產生異或的結果肯定是0。而所求的那兩個數字當中只有一個數字的第3位1,不妨假定這個數字是a(此時未知),另外一個要求的數字是b(此時未知),很明顯,將a (此時未知)和0111100b異或就可以得到b (此時已知),再用0111100b和已經求出來的b (此時已知) 進行異或就可以得到a (此時已知)。比如上面的95的第3位爲1,所以用95 ^ 0111100b = 1011111b ^  0111100b = 1100011b = 99,再用99 ^ 0111100b = 1100011b ^ 0111100b = 1011111b = 95。

 

有了上面的解題思路,代碼就好寫了。參考代碼如下:

#include <iostream>
using namespace std;

void get_2_numbers(int* arr, int size, int& number1, int& number2)
{
	// 異或結果從右往左數第一個爲1的bit的位置
	int pos = 0;

	// 異或結果
	int exclusive_or = 0;

	// 求出數組中各元素的異或
	for(int i = 0; i < size; ++i)
	{
		exclusive_or ^= *(arr + i);
	}

	number1 = exclusive_or;			// 臨時保存異或結果
	/*
	while((exclusive_or & 1) == 0)	// 如果最右一bit是0,則循環
	{
		++pos;
		exclusive_or = exclusive_or >> 1;
	}
	// 循環結束時,得到的pos就是,異或結果中,從右往左數,第一個爲1的bit的位置
	*/
	pos = exclusive_or & (-exclusive_or);   // 根據kingbigeast的建議,用這一行代碼代替上面的while循環

	exclusive_or = number1;
	for(int i = 0; i < size; ++i)
	{
		if((*(arr + i) >> pos) & 1)
		{
			number1 ^= *(arr + i); 
		}
	}

	number2 = number1 ^ exclusive_or;

	return;
}

int main(int argc, char**)
{
	// 要求的兩個數字
	int a, b;

	// 下面數組模擬大數組中,有兩個不同的元素95、99,其它元素皆成對出現
	int arr[12] = {1,2,3,4,5,95,99,5,4,3,2,1};
	get_2_numbers(arr, sizeof(arr) / sizeof(int), a, b);

	cout << "所求的兩個數字是:" << endl;
	cout << a << "\t" << b << endl;

	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章