1. 程式人生 > >劍指offer--查詢只出現一次的數字

劍指offer--查詢只出現一次的數字

題目:一個整形數組裡面,只有兩個數字出現了一次,其他的數字都出現了兩次,求只出現一次的數。

分析:首先考慮這個問題的一個簡單版本,一個數組裡除了一個數字外,其他的數字都出現了兩次。請寫程式找出只出現一次的數字?

        這個問題的突破口在哪裡?為什麼別的數字都是出現了兩次?這裡就是為了讓我們想到異或運算的性質:任何一個數字異或它自己都等於0,若沒有這個特性,我們可以迴圈遍歷陣列,給每個出現的數記一次數,可以用hashmap的key,value實現。但是有了這個特性我們就要想到更高效的演算法。即從頭到為依次異或陣列中的每一個數字,那麼最終的結果剛好是那個只出現一次的數字,出現兩次的數字全部在異或中抵消掉了。

        有了上面這個簡單的解決方案之後,回到原始問題。我們能不能把原陣列分為兩個子陣列。每個子陣列中,包含一個只出現一次的數字,而其他數字都出現了兩次,如果能這樣拆分原陣列,按前面的辦法就可以分別求出這兩個只出現一次的數字。

        所以,第一步還是從頭到尾依次異或陣列中的每一個數字,那麼最終得到的結果就是兩個只出現一次的數字的異或結果。因為其他數字都出現了兩次,在異或中全部抵消掉了。第二步,由於這兩個數字肯定不一樣,那麼異或結果肯定不為0,也就是說這個結果數字的二進位制表示中至少有一位是1.,所以我們在異或結果數字中找到第一個1的位置,記為第N位。第三步,以這第N位為標準將陣列分為兩個部分。在二進位制第N位為1的都放在一個數組,為0的放到另一個數組,這樣兩個子陣列中就各包含一個只出現一次的數字,這邊大家要想一下,因為是進行異或操作,所以在第N位,兩個只出現一次的數字肯定不一樣,有了這個異或的性質才能這樣分組。

//num1,num2分別為長度為1的陣列。傳出引數
//將num1[0],num2[0]設定為返回結果
public class Solution {
     public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
       if(array==null ||array.length<2)
           return ;
       int temp = 0;
//依次異或陣列中的數,得到結果為兩個只出現一次的數字的異或結果
       for(int i=0;i<array.length;i++)
           temp ^= array[i];
        //獲取第一個1在異或結果中出現的位置
       int indexOf1 = findFirstBitIs(temp);
       for(int i=0;i<array.length;i++){
            //異或第一個陣列得到第一個出現一次的值
           if(isBit(array[i], indexOf1))
               num1[0]^=array[i];
           else
               num2[0]^=array[i];
       }
   }
    
//獲取第一個1在異或結果中出現的結果
public int findFirstBitIs(int num){ int indexBit = 0;
        //num只要還不為0,就右移1為,計數Index+1,這樣就可以得到第一個1出現的位置
       while(((num & 1)==0) && (indexBit)<8*4){
           num = num >> 1;
           ++indexBit;
       }
       return indexBit;
   }
//判斷第N為是否為1,作為分類標準
   public boolean isBit(int num,int indexBit){
       num = num >> indexBit;
       return (num & 1) == 1;
   }

}