用位運算巧解算法題目!

這裏分享一道leetcode裏的算法題目,鏈接leetcode

這個題目很巧妙的利用了位運算。

一個整型數組 nums 裏除兩個數字之外,其他數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。

示例 1:
輸入:nums = [4,1,4,6]
輸出:[1,6][6,1]


示例 2:
輸入:nums = [1,2,10,4,1,4,3,3]
輸出:[2,10][10,2]

如果用容器,比如HashMap,那空間複雜度不可能是O(1)。看用位運算是如何解決這個問題的。

java異或運算(^)

異或的運算方法,是二進制運算。

1^1 = 0 ,
0^0 = 0,
1^0 = 1,
0^1 = 1
簡單一句話,相同爲0,不同爲1

舉個例子, 4^3

4的二進制:100
6的二進制:110
然後看4^6 :010 轉化成十進制就是2

異或運算法則
  1. a ^ b = b ^ a
  2. a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
  3. d = a ^ b ^ c 可以推出 a = d ^ b ^ c.
  4. a ^ b ^ a = b

很顯然:
a^a = 0,即與自身異或,得0;
a^0 = a,即與0異或,得自身

回到本文的那個算法題目,如果是,數組裏,僅有一個出現了一次,其它都出現了兩次,那將數組裏所有的數字異或,最後的結果就是那個出現一次的。

比如[1,2,3,4,3,2,1]  那麼:1^2^3^4^3^2^1 = 1^1^2^2^3^3^4 = 4    因爲a^a = 0  a^0 = a

如果這個理解的話,這個算法題目就可以入手了。把數組分成兩部分,每部分中都滿足:僅有一個數字出現一次,其它出現兩次。那就可以找到這兩個數字了。

怎麼分成兩部分呢? 假設數組nums中,a和b僅出現了一次。

nums中所有數字異或結果=a^b

假如 a^b = 2(二進制10),即二進制低二位上,a和b不一樣。那麼 a&2 和 b&2 一定不相等。這樣就把a和b分開了。 ----即二進制位中,找一個是1的。

nums中,其它的數字,同樣和2做相與操作(x&2),即可分成兩部分。
文字敘述不好理解,咱們上代碼

    public int[] singleNumbers(int[] nums) {
        if(null == nums){
            return null;
        }
        int bit = 0; // 所有數字異或的結果
        for(int num : nums){
            bit ^= num;
        }
        int last = 1; // 獲取異或結果中低位1
        while((last & bit) == 0){
            last <<= 1;
        }
        int a = 0;
        int b = 0;
        for(int num : nums){
        	// 分成兩組(a和b如何分,上文說過了。同一個數字,相與num,結果一定相同,即一定分到同一組)
            if((last & num) == 0){
                a ^= num;
            }else{
                b ^= num;
            }
        }
        return new int[]{a,b};
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章