1. 程式人生 > >陣列中有重複數字系列題

陣列中有重複數字系列題

1.陣列中只有一個數字出現1次,其他數字出現兩次,找到只出現一次的這個數字。(題目還可以改成只有一個數字出現2(1)次,其他數字出現3次,找出那個except one)
思路:這類題實際是位運算的題,所有數字出現兩次,只有個出現一次, 異或操作,都異或一遍,最後剩下的就是要求的那個數字了,那如果所有數字都出現3次呢?
可以實際不論是出現兩次還是三次,我們的目的都是把這些消除掉,求最後剩下的那個,那麼可以看成是二進位制求模運算,陣列中的數字都是32位int型別,那麼一個數字如果出現兩次,對應二進位制上為1的位就出現2次,這時這個位應該消除為0(每個數字只出現兩次),所以就是二進位制模2,那麼每個數字都出現3次就是二進位制模3.
對於每個數字出現三次的題目,難點在於我們怎麼找到一種運算等價於對數字的每個位做二進位制模3呢?用兩個數字ones,twos記錄每一位當前的狀態,用X表示下一位到來的是0還是1,可以寫一個真值表啦,通過真值表能求得模3的邏輯表示式。那麼最後ones代表的數就是隻出現一次的那個數,而twos代表的就是隻出現兩次的那個數啦(是所有數出現三次而只有一個出現1次或者只有一個出現2次)。

2.這類題還有一個變種,就是說,所有數出現兩次,有兩個數出現1次。怎麼求呢?先異或一遍得到一個數num,然後分成兩隊再分別異或,通過num最右邊第一個出現的1的那個二進位制位可以劃分啦!

3.一個數組,1<=a[i]<=n,n是陣列長度,有的數出現兩次有的數出現1次,怎麼找出出現兩次的所有數?O(n) time, O(1)space.
思路:法一:找特點,陣列下標是0~n-1,陣列內容是1~n,有重複,

for(int i=0; i<arr.size(); i++){
        while(arr[i] != arr[arr[i]-1]){
            swap(arr[i], arr[i]);
        }
}

這樣就可以出現一次的數字比下標大1,如果不滿足,一定是重複數字了。
法二:既然不能申請額外空間,那就原地當雜湊表,

for(int i=0; i<arr.size(); i++){
        int index = arr[i]-1;
        if(arr[index] > 0){
            arr[index] = 0-arr[index];
        }else{
            res.push_back(index);
        }
    }

如果再次碰見負數說明這個下標位置已經找過一次了,重複。利用的是陣列下標唯一併且數字範圍的特點。