1. 程式人生 > >經典演算法題1:找出陣列中只出現一次的數字,其它數字都出現了兩次

經典演算法題1:找出陣列中只出現一次的數字,其它數字都出現了兩次

題目:一個整型數組裡除了一個數字之外,其它的數字都出現了兩次。請寫程式找出這個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。

分析:由於題目要求時間複雜度為O(n),所以先排序然後比較相鄰數字是否相同的思路被排除。

            空間複雜度是O(1),輔助空間被限制,所以hash表的思路也被排除。

           那麼這個題的突破口在哪裡呢?注意這個陣列的特殊性:其它數字都出現了兩次,只有一個數出現了一次。可以想到運用異或運算,任何一個數字異或它自己都等於0。

           如果我們從頭到尾依次異或陣列中的每一個數,那麼最終的結果就是那個只出現一次的數字,因為其他出現兩次的數字全部在異或中被抵消為0了(異或運算遵循交換分配率)。

           舉個栗子:2  3  4  2  3

           所有數字依次異或運算:2 xor 3 xor 4 xor 2 xor 3 = (2 xor 2) xor (3 xor 3) xor 4= 0 xor 0 xor 4 = 4

          最終結果4就是我們要找的那個只出現一次的數字。

void FindNumsAppearOnce(int data[], int length, int &num1)
{
	if (length < 2)
		return;
	int resultExclusiveOR = 0;
	for (int i = 0; i < length; ++ i)
		resultExclusiveOR ^= data[i];
        num1=resultExclusiveOR ;
}

此題的擴充套件:一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。

分析:數組裡包含了兩個只出現一次的數字,那麼所有數字依次異或的結果就是這兩個只出現一次的數字的異或結果。我們要想辦法利用這個異或結果,把陣列分為兩個子陣列,一個子陣列包含一個只出現一次的數字,另一個數組包含另一個只出現一次的數字。由於這兩個只出現一次的數字肯定不相等,那麼這個異或結果肯定不為0,也就是說在這個結果數字的二進位制表示中至少就有一位為1。我們在結果數字中找到第一個為1的位的位置,記為第N位。現在我們以第N位是不是1為標準把原陣列中的數字分成兩個子陣列,第一個子陣列中每個數字的第N位都為1,而第二個子陣列的每個數字的第N位都為0。

           舉個栗子:1 2 3 4 2 3

          所有數字異或結果 = 1 xor 4

           二進位制表示為:  001

                                xor 100

                                _______

                                       101

         異或結果的二進位制位101,第一個為1的位的位置N=1。

         那麼,陣列所有數的二進位制表示中,第N(N=1)位為1的的數為1 3 3,第N(N=1)位為0的的數為2 2 4.

         這樣就把原陣列拆分為兩個子陣列,1和4被分到不同的陣列中去了。然後分別在兩個子陣列中找只出現一次的那個數。

void FindNumsAppearOnce(int data[], int length, int &num1, int &num2)
{
	if (length < 2)
		return;
 
	// get num1 ^ num2
	int resultExclusiveOR = 0;
	for (int i = 0; i < length; ++ i)
		resultExclusiveOR ^= data[i];
 
	// get index of the first bit, which is 1 in resultExclusiveOR
	unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);	
 
	num1 = num2 = 0;
	for (int j = 0; j < length; ++ j)
	{
		// divide the numbers in data into two groups,
		// the indexOf1 bit of numbers in the first group is 1,
		// while in the second group is 0
		if(IsBit1(data[j], indexOf1))
			num1 ^= data[j];
		else
			num2 ^= data[j];
	}
}
 
// Find the index of first bit which is 1 in num (assuming not 0)
unsigned int FindFirstBitIs1(int num)
{
	int indexBit = 0;
	while (((num & 1) == 0) && (indexBit < 32))
	{
		num = num >> 1;
		++ indexBit;
	}
 
	return indexBit;
}
 

// Is the indexBit bit of num 1?
bool IsBit1(int num, unsigned int indexBit)
{
	num = num >> indexBit;
 
	return (num & 1);
}