演算法之位運算

it_was發表於2020-10-09

巧妙地利用位元算的特性可以以極低的時空複雜度解決問題:squirrel:

給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。

思路: 位運算——陣列中兩兩異或即可!

因為有 a ^ a = 0,0 ^ a = a ,並且異或運算滿足交換律 故對陣列[a,b,c,a,d,d,b] 透過異或運算: a ^ b ^ c ^ a ^ d ^ d ^ b -> a ^ a ^ b ^ b ^ d ^ d ^ c -> 0 ^ 0 ^ 0 ^ c - > c !

給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現了三次。找出那個只出現了一次的元素。

思路:求出每一位和,對3取餘即可!

因為只有一個數字出現一次,其他都是三次,說明在每一位上,如果特殊數字出現,那麼這一位上 1 的個數一定不是 3 的倍數!相反若其他數字在某位出現而特殊的沒有出現,則該位上的 1 的數量一定是 3 的倍數
演算法之位運算

 public static int singleNumber (int[] A) {
        // write code here
        if(A == null || A.length == 0){
            return -1;
        }
        int res = 0;
        for(int i = 0;i < 32;i++){ //整數一共32位!所以每一位都得需要統計
            int sum = 0;
            for(int j = 0; j< A.length;j++){ 
                sum += ((A[j] >> i) & 1); //統計第 i 位上 1 的數量
            }
            res += ((sum % 3)<<i); //注意左移i過去的需要右移i回來!!!!
        }
        return res;
    }

延伸一下,對於某陣列若只有一個數字出現一次,其他數字都出現 n 次 (n > 1),則都可以透過統計每一位上 1 的數量來做!

給定一個整數陣列 nums,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。 找出只出現一次的那兩個元素。
輸入: [1,2,1,3,2,5]
輸出: [3,5]

思路:結合 題目(只出現一次的數字 I) 來做

因為本題有兩個數字(假設位a和b)只出現一次,其他都出現兩次,所以根據題目一可以馬上得到這兩個特殊數字的異或結果,即為 a ^ b。之後怎麼做?我們知道兩數的異或結果代表了兩個數字在某些位上的不同!
所以根據這個性質我們可以將該陣列分成兩部分,這兩部分分別只有一個特殊數字!之後分別全部異或一下就得到兩個特殊數字!
可以透過 a ^ b 從右開始的第一個出現的 1 所在的 位 作為區分標準!因為這個 1 代表了 a 和 b 在當前位的不同:boom:

public int[] singleNumber(int[] nums) {
        if(nums == null || nums.length <= 1){
            return new int[]{};
        }
        int xor = 0;
        for(int n : nums){
            xor ^= n; //求出兩數的異或
        }
        int bit = 1;
        while((xor & 1) == 0){
            //從左往右尋找第一個 1
            xor >>=1;
            bit <<= 1;
        }
        int val1 = 0;
        int val2 = 0;
        for(int n : nums){
            if((n & bit) == 0){ //如果和當前bit位相同,則歸為一類
                val1 ^= n;
            }else{ //否則歸為另一類
                val2 ^= n;
            }
        }
        return new int[]{val1,val2};
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章