1. 程式人生 > >劍指offer:陣列中只出現一次的數字(java)

劍指offer:陣列中只出現一次的數字(java)

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

    如輸入陣列{2,4,3,6,3,2,5,5},因為只有4,6這兩個數字只出現一次,其他數字都出現了兩次,所以輸出4,6

    這是一個比較難的題目,很少有人在面試的時候不需要提示一下子想到最好的解決辦法。一般當應聘者想了幾分鐘那個後還沒有思路,面試官會給出一些提示。

     我們想到異或運算的一個性質:任何一個數字異或它自己都等於0,也就是說,如果我們從頭到尾依次異或陣列中的每一個數字,那麼最終的結果剛好是哪個出現一次的數字,因為那些成對出現的兩次的數字都在異或中低消了。

    我們試著把陣列分成兩個子陣列,使得每個子陣列只包含一次出現一次的數字,而其他數字都成對出現兩次。如果能夠這樣拆分成兩個陣列,我們就可以按照前面的辦法分別找出兩個只出現一次的數字了。

我們還是從頭到尾依次異或陣列中的每一個數字,那麼最終得到的結果就是兩個只出現一次的數字的異或的結果。因為其他數字都出現兩次,在異或中全部抵消了。由於這兩個數字肯定不一樣,那麼異或的結果肯定不為0,也就是說在這個結果數字的二進位制表示中至少有一位為1.我們在結果數字中找到最右邊為1的位的位置,記為第n位。現在我們以第n位是不是1為標準把原陣列中的數字分成兩個子陣列,第一個子陣列中的每個數字的第n位都是1,而第二個子陣列中每個數字的第n位都為0.由於我們分組的標準是數字中的某一位是1還是0,那麼出現了兩次的數字肯定被分配到同一個子陣列中。因為兩個相同的數字的任意一位都是相同的,我們不可能把兩個相同的數字分配到兩個子陣列中去,於是我們已經把原陣列分成了兩個子陣列,每個子陣列都包含了一個只出現一次的數字,而其他數字都出現了兩次。我們已經知道如何在陣列中找出唯一一個只出現一次的數字,因此到此為止所有的問題都解決了。

 public void findNumsAppearOnce(int[] arr){  
        if(arr == null)  
            return;  
        int number = 0;  
        for(int i: arr)  
            number^=i;  
        int index = findFirstBitIs1(number);  
        int number1= 0,number2 = 0;  
        for(int i : arr){  
            if(isBit1(i,index))  
                number1^=i;  
            else  
                number2^=i;  
        }  
        System.out.println(number1);  
        System.out.println(number2);  
    }  
    private int findFirstBitIs1(int number){  
        int indexBit = 0;  
        while((number & 1)== 0){  
            number = number >> 1;  
            ++indexBit;  
        }  
        return indexBit;  
    }  
    private boolean isBit1(int number,int index){  
        number = number >>index;  
        return (number & 1) == 0;  
    }