【劍指offer】陣列中只出現一次的數字(2)
轉載請註明出處:http://blog.csdn.net/mmc_maodun/article/details/27800577
題目:一個int陣列中有三個數字a、b、c只出現一次,其他數字都出現了兩次。請找出三個只出現一次的數字。
上篇博文中我們求的是兩個只出現一次的數字,且時間複雜度為O(n),這次是三個,可以同樣考慮將陣列先分成兩個子陣列,求出其中一個只出現一次的數字,而後再將另一個子陣列分成兩個子陣列,再分別求這兩個只出現一次的數字。何海濤的部落格給的就是這種思路,並給出了詳細的證明過程,詳見:http://zhedahht.blog.163.com/blog/static/25411174201283084246412/,但該方法真的要自己去想,很難想到,而且如果是面試的話,很難給面試官講懂。下面介紹另外一種方法,該方法具一定的通用性,對於2個,3個出現一次的數字這類的問題,都可以按照該思路去求解,只是時間複雜度可能要稍微大些,為O(8*sizeof(int)*n),8*sizeof(int)其實即使int的位,在一般的32位系統中,它為32,而n自然就是陣列的長度了。
該方法的思路如下:
首先由於有3個數字出現一次,其他的都出現兩次,所以n肯定為奇數,該方法通過掃描整數的每一位來逐個判斷。
再看這3個只出現一次的數字,他們的bit位肯定不可能全部相同,也就是說,雖然有些bit位上的數可能相等,但肯定至少存在某一個bit位,這三個數中,有兩個數的該bit位為1,一個數的該bit位為0,或者兩個數的該bit位為0,一個數的該bit位為1。我們可以通過掃面int的所有bit位,掃描每個bit位的時候,遍歷陣列,如果能找出符合上面條件的,我們就可以找出其中的一個只出現一次的數字,該數字與另外兩個只出現一次的數的bit位不同。找到一個之後,就可以將其與陣列的最後一個元素交換,再在前面n-1個數中找出另外兩個就可以了,方法的話,可以直接用上篇博文中介紹的方法,也可以用該博文介紹的思路。
下面要來看下如果找出這個與另外兩個數的該bit位不同的數。
先看第一種情況,如果a,b,c三個數中,有兩個該bit位為0,另一個為1,我們遍歷陣列,分別統計該陣列元素中該bit位為1和0的元素個數,分別設為count1和count0,並同時將所有該bit位為1的元素異或,所有該bit位為0的元素異或,得到的結果分別設為temp1和temp0。如果count1為奇數,則可能有兩種情況,a,b,c三個數的該bit位全為1,或者有兩個為0,一個為1,如果有temp0==0,則說明是前一種情況(a,b,c的該bit位全為1的話,所有該bit位為0的每個元素出現了兩次,因此異或後的結果為0),此時沒法找出其中的一個數,則直接跳到下次迴圈,繼續判斷下一個bit位,如果temp0!=0,則說明是後一種情況(說明該比bt位為0的元素異或後沒有完全抵消,則說明有一個元素是隻出現一次的),此時其中一個只出現一次的數字就是temp0(重複的元素異或後都抵消了)。
第二種情況,是兩個該bit位為1,另一個為0的情況,分析思路與上面的類似。
很明顯,這種掃描每個bit位來進行判斷的思路可以解決整個的這一類問題,自然也可以求出上篇博文中兩個只出現一次的數字。具體思路不再給出,主要的判斷依據,一般都是bit位為1或0的數字的個數的奇偶,bit位為1的元素異或,bit位為0的元素異或後的結果是否為0的判斷。
下面給出這道題目的完整程式碼:
#include<stdio.h>
/*
通過掃面每一位,先找出一個只出現一次的數
*/
int FindOneNumAppearOnce(int *arr,int len)
{
int count1 = 0; //某一位上1的個數
int count0 = 0; //某一位上0的個數
int temp1 = 0; //某一位為1的所有數相異或的結果
int temp0 = 0; //某一位為0的所有數相異或的結果
int i,j;
for(i=0;i<8*sizeof(int);i++) //迴圈計算每一位的以上四個資料
{
count1 = count0 = temp1 = temp0 = 0;//每次計算下一位時清零
for(j=0;j<len;j++)
{
//每次向左移一位進行計算
if(arr[j] & (1<<i)) //該位為1時
{
temp1 ^= arr[j];
count1++;
}
else
{
temp0 ^= arr[j];
count0++;
}
}
if(temp1 & 1) //某位上有奇數個1
{
if(temp0 == 0) //此時3個不同數的該位都為1
continue;
else //此時3個不同數的該位有1個1,2個0
return temp1;
}
else //某位上有偶數個1
{
if(temp1 == 0) //此時3個不同數的該位都為0
continue;
else //此時3個不同數的該位有1個0,2個1
return temp0;
}
}
}
/*
返回num的最低位的1,其他各位都為0
*/
int FindFirstBit1(int num)
{
//二者與後得到的數,將num最右邊的1保留下來,其他位的全部置為了0
return num & (-num);
}
/*
判斷data中特定的位是否為1,
這裡的要判斷的特定的位由res確定,
res中只有一位為1,其他位均為0,由FindFirstBit1函式返回,
而data中要判斷的位便是res中這唯一的1所在的位
*/
bool IsBit1(int data,int res)
{
return ((data&res)==0) ? false:true;
}
void FindTwoNumsAppearOnce(int *arr,int len,int *num1,int *num2)
{
int i;
int AllXOR = 0;
//全部異或
for(i=0;i<len;i++)
AllXOR ^= arr[i];
int res = FindFirstBit1(AllXOR);
*num1 = *num2 = 0;
for(i=0;i<len;i++)
{
if(IsBit1(arr[i],res))
*num1 ^= arr[i];
else
*num2 ^= arr[i];
}
}
/*
交換兩個int變數
*/
void Swap(int *a,int *b)
{
if(*a != *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
/*
找出這三個只出現一次的數字
*/
void FindThreeNumsAppearOnce(int *arr,int len,int *num1,int *num2,int *num3)
{
if(arr==NULL || len<3)
return;
*num1 = FindOneNumAppearOnce(arr,len);
//找到第一個找出的數字,並與最後一個元素交換,便於接下來剩下的兩個數字
int i;
for(i=0;i<len;i++)
if(*num1 == arr[i])
break;
Swap(&arr[i],&arr[len-1]);
FindTwoNumsAppearOnce(arr,len-1,num2,num3);
}
int main()
{
static int arr[1000000];
int n;
while(scanf("%d",&n) != EOF)
{
int i;
for(i=0;i<n;i++)
scanf("%d",arr+i);
int num1,num2,num3;
FindThreeNumsAppearOnce(arr,n,&num1,&num2,&num3);
printf("%d %d %d\n",num1,num2,num3);
}
return 0;
}
測試結果:相關文章
- 劍指offer 陣列中只出現一次的數字陣列
- 《劍指offer》:[40]陣列中只出現一次的數字陣列
- 【劍指offer】陣列中只出現一次的數字(1)陣列
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 劍指Offer--陣列中重複的數字陣列
- 《劍指offer》:[51]陣列中的重複數字陣列
- 【劍指offer】數字在排序陣列中出現的次數排序陣列
- 劍指offer:旋轉陣列的最小數字陣列
- 劍指offer 旋轉陣列的最小數字陣列
- 找出陣列中只出現一次的數字陣列
- 《劍指offer》:[38]數字在排序陣列中出現的次數排序陣列
- 劍指offer-轉陣列的最小數字-php陣列PHP
- 劍指Offer-39-數字在排序陣列中出現的次數排序陣列
- 劍指OFFER-數字在升序陣列中出現的次數(Java)陣列Java
- 【劍指offer】第一個只出現一次的字元字元
- 【劍指offer】7.旋轉陣列的最小數字陣列
- 劍指 Offer 11. 旋轉陣列的最小數字陣列
- 【劍指 Offer】11. 旋轉陣列的最小數字陣列
- JZ-040-陣列中只出現一次的數字陣列
- 【劍指offer】陣列中的逆序對陣列
- Leetcode 劍指 Offer 03. 陣列中重複的數字LeetCode陣列
- 劍指offer刷題之路--1.陣列中重複的數字陣列
- 【劍指offer】把陣列排成最小的數陣列
- [劍之offer] 03 陣列中重複的數字陣列
- 劍指offer之列印超過陣列一半的數字陣列
- 劍指 offer(1) -- 陣列篇陣列
- 【劍指offer】二維陣列中的查詢陣列
- [劍指Offer]面試題35:第一個只出現一次的字元面試題字元
- 劍指Offer-把陣列中的數排成一個最小的數陣列
- 劍指Offer-37-陣列中逆序對陣列
- [劍指offer題解][Java]陣列中出現次數超過一半的數字Java陣列
- 劍指Offer-34-把陣列排成最小的數陣列
- 劍指offer——把陣列排成最小的數C++陣列C++
- 力扣 - 劍指 Offer 39. 陣列中出現次數超過一半的數字力扣陣列
- 劍指offer——陣列中的逆序對C++(75%)陣列C++
- 【劍指offer】【1】二維陣列中的查詢陣列
- (python版)《劍指Offer》JZ06:旋轉陣列的最小數字Python陣列
- 《劍指offer》:[41]陣列中和為S的兩個數陣列