數組中,只有一個/兩個/三個數只有一個,其他都爲偶數個,找出只有一個的數

解決方案1:

先排序,後比較,時間複雜度爲O(nlogn+n)

解決方案2:

採用位運算。

因爲 0^ a=a, a^a=0;

所以只有1個不同時,循環異或一遍,結果便是。


只有兩個不同時,循環異或一遍,結果便是x=a^b,因爲a!=b,所以x中必存在某一位爲1,假設爲第k位。那麼a,b的第k位一個比爲0,另一個必爲1。

這樣我們就可以根據第k位爲0,還是1,將數組中所以元素劃分爲兩個陣營,一個是第k爲0,爲a,c,c,d,d,.另一個是第k位爲1,爲b,e,e,f,f,..。這樣我們就轉化爲第一個的問題了。

在每個陣營中循環異或一遍便得到相應的結果。


只有三個不同時,循環異或一遍,結果便是x=a^b^c,我們想可不可以轉化爲前面的問題。但是我們發現不能直接採用第二個問題的思路。

因爲:1。如果x中存在某一位爲1,假設爲第k位。那麼a,b,c的第k位爲1,00或者111這兩種形式。對於第一種可以採用劃分的思路,將三個陣營縮小爲兩個陣營,即轉化爲第二個問題和第一個問題。對於第二種形式無法劃分成兩個陣營。

   2。x中必存在某位爲1嗎?答案爲可能x的結果就是0

所以該思路不行。

我們可以這樣,設數組爲a,b,c,d,d,e,e,f,f,..先循環異或一遍,結果便是x=a^b^c,然後用x去異或數組中每個元素。即x^a,x^b,x^c,x^d,x^d...

即把原先數組爲現在的數組A,B,C,D,D,E,E,F,F其中A=b^c, B=a^c, C=a^b,且A!=0,B!=0,C!=0(因爲a,b,c 不相同)

然後對現在數組再異或一遍,結果爲y=A^B^C=0

這時候我們可以把A^B看做一個整體,因爲異或結果爲0,則數必相同,即A^B=C。

由於C!=0,則C必存在某一位爲1,假設爲第k位。即A^B的第k位爲1,則A,B的第k爲要麼爲0要麼爲1,這樣我們就可以把A,B,C,D,D,E,E,F,F數組中元素根據第k位爲0還是爲1劃分爲兩個陣營,一個陣營中第k爲1,有兩個不同,一個陣營第k爲0,有一個不同,直接分別使用我們前面的一個不同,兩個不同的方法,我們就可以獲得A,B,C,

由A,B,C獲得a,b,c 這個就比較簡單了。a=A^x,b=B^x,c=C^x

思路沒問題了,但是在編程中,如果找到C,這是個問題。下面是小小的技巧使用。

    我們定義一個函數f(n),它的結果是保留數字n的二進制表示中的最後一位1,而把其他所有位都變成0。比如十進制6表示成二進制是0110,因此f(6)的結果爲2(二進制爲0010)。f(x^a)f(x^b)f(x^c)的結果均不等於0。

但是我們知道A,B,C中的f(A), f(B), f(C)的值必有兩個相同,z=f(A)^f(B)^f(C) 我們就可以把數組A,B,C,D,D,E,E的函數值即f(A), f(B), f(C),f(D), f(D), f(E), f(E),根據他們的值和z相不相同進行劃分。

#include<stdio.h>
int findOneNumber(int arr[],int n, int *num)
{
	int i;
	*num=0;
	for( i=0; i<n; i++)
	{
		*num=arr[i] ^ *num;
	}
	return 0;
}

int findFirstBitIs1(int num)
{
	int count=0;
	while( (num & 1)==0 && count <32)
	{
		num=num >> 1;
		count++;
	}
	return count;
}
int bitkIs1(int num, int k)
{
	while(k)
	{
		num=num >> 1;
		k--;
	}
	if( (num & 1)==1)
		return 1;
	else
		return 0;
}

int findTwoNumber(int arr[], int n, int *num1, int *num2)
{
	int i;
	int temp=0;
	for(i=0; i<n ;i++)
		temp=arr[i] ^ temp;
	int bitk=findFirstBitIs1(temp);
	int array1[n];
	int array2[n];
	int k1=0;
	int k2=0;
	for(i=0; i< n; i++)
	{
		if(bitkIs1(arr[i], bitk))
		{
			array1[k1++]=arr[i];
		}
		else
		{
			array2[k2++]=arr[i];
		}
	}
	findOneNumber(array1, k1, num1);
	findOneNumber(array2, k2, num2);
	return 0;
}

int findThreeNumber(int arr[], int n, int *num1, int *num2, int *num3)
{
	int temp=0;
	int i;
	for(i=0; i<n; i++)
		temp=temp ^ arr[i];
	//相當於構造了一個新的數組.原數組相當於a,b,c,d,d,e,e,現在爲A,B,C,D,D,E,E
	for(i=0; i<n ;i++)
		arr[i]=arr[i] ^ temp;
	int flip=0;
	for(i=0; i<n; i++)
		flip=flip ^ findFirstBitIs1(arr[i]);

	int array1[n];
	int array2[n];
	int k1=0;
	int k2=0;
	//劃分爲兩個部分,其實也可以優化空間使用情況,但是代碼可讀性差。
	for(i=0; i< n; i++)
	{
		if( findFirstBitIs1(arr[i])==flip )
		{
			array1[k1++]=arr[i];
		}
		else
		{
			array2[k2++]=arr[i];
		}
	}
	if(k1 % 2==0)
	{
		findTwoNumber(array1, k1, num1, num2);
		findOneNumber(array2, k2, num3);
	}
	else
	{
		findOneNumber(array1, k1, num1);
		findTwoNumber(array2, k2, num2, num3);
	}
	*num1 ^=temp;
	*num2 ^=temp;
	*num3 ^=temp;
	return 0;

}
int main()
{
	int a[]={2, 1, 2, 1, 5, 1, 5, 1, 4};
	int len=sizeof(a)/sizeof(int);
	int num1,num2,num3;
	findOneNumber(a, len, &num1);
	printf("number is %d \n",num1);

	int b[]={2, 4, 2, 2, 2, 2, 4, 4};
	len=sizeof(b)/sizeof(int);
	findTwoNumber(b, len, &num1, &num2);
	printf("num1: %d num2: %d\n",num1, num2);

	int c[]={1, 4, 2, 4, 4, 5, 5};
	len=sizeof(c)/sizeof(int);
	findThreeNumber(c, len, &num1, &num2, &num3);
	printf("num1: %d num2: %d num3: %d\n", num1, num2, num3);
	//printf("%dth bit is 1 \n" ,findFirstBitIs1(4));
	return 0;
}





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