劍指offer 陣列中只出現一次的數字

bigbigship發表於2015-05-04

題目描述:                       

一個整型陣列裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。
輸入:                       
每個測試案例包括兩行:
第一行包含一個整數n,表示陣列大小。2<=n <= 10^6。
第二行包含n個整數,表示陣列元素,元素均為int。
輸出:                       
對應每個測試案例,輸出陣列中只出現一次的兩個數。輸出的數字從小到大的順序。
樣例輸入:                       
            8
            2 4 3 6 3 2 5 5
樣例輸出:                       
            4 6
 
先看一個簡單點的問題,如果只有一個數出現了一次,那麼狠明顯把所有的數異或起來就得到了這個只出現了一次的數。
 
那麼再來看這個問題,如果我們把所有的數異或起來,那麼我們得到的就是這兩個數異或起來的值,設這兩個數為x,y.
 
a1^a2^a3^...^an=x^y = z != 1.
 
那麼我們設想能不能將其分成兩類。我們找到z的最低位的1然後對其進行分析,可以得出
1.這n個數中這一位為1的數一定有奇數個,
2.x,y中一定有一個數的這一位為1,另一個這一位為0.
3.那麼這n個數種這一位為0的肯定也有奇數這。
那麼我們就將其分成了兩類了。
將這一位為1的全部異或起來,將這一位為0的全部異或起來,分別可以得到一個數。
 
程式碼如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1e6+10;

int a[maxn];

int main()
{
    int n;
    while(~scanf("%d",&n)){
        int ans= 0,num1=0,num2=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            ans^=a[i];
        }
        int x = ans&(-ans);
        for(int i=0;i<n;i++){
            if(x&a[i]) num1^=a[i];
            else num2^=a[i];
        }
        printf("%d %d\n",min(num1,num2),max(num1,num2));
    }
    return 0;
}

 

我們繼續分析這個問題。如果有三個數出現的次數為1次,剩下的都是兩次呢?

我們接著上面的思路進行分析。這這三個數為x,y,z.

a[1]^a[2]^...^a[n] = ans = x^y^z != 0;

根據這個我們可以得出

ans^x != 0, ans^y !=0 , ans^z !=0;

 

然後我們根據上面的思路分析 取ans的最低位1,因為ans>0因此肯定存在一個最低位的1

那麼又因為 ans^x^ans^y^ans^z = ans 那麼就存在兩種情況,

1) ans^x, ans^y, ans^z的這一位全為1.

證明:如果滿足,ans^x,ans^y,ans^z的這一位都是1的情況下那麼x,y,z的這一位就都是0了,又因為ans=x^y^z,

如果這一位都是0的話那麼ans的這一位肯定是0,相互矛盾,故不可能。

2) 其中一個的這一位為1,剩下的兩個的這一位為0.

根據這一點我們可以將其分成兩類了。然後將這n個數這一位為1的進行異或可以求出一個值。

算出來之後將這個值加入到陣列中,就只有兩個數未知了,根據上面的解法就可以求出來了。

程式碼如下:

#include <iostream>
#include <cstdio>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1e6+10;

int a[maxn];

int main()
{
    int n;
    while(~scanf("%d",&n)){
        int tot = 0,x=0,y=0,z=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            tot^=a[i];
        }
        int m=tot&(-tot);
        for(int i=0;i<n;i++)
            if(m&a[i]) x^=a[i];
        tot^=x;
        a[n]=x;
        m = tot&(-tot);
        for(int i=0;i<=n;i++){
            if(m&a[i]) y^=a[i];
            else z^=a[i];
        }
        printf("%d %d %d\n",x,y,z);
    }
    return 0;
}
/****
5
1 1 2 3 4

3
7 8 9

7
11 11 2 2 3 4 5

9
1 1 2 2 3 3 4 5 6
***/


 

 

相關文章