leetcode 算法解析(一):260. Single Number III

260.Single Number II 原題連接java

  • 本題其實算是比較簡單,在 leetcode 上也只是 medium 級別,ac 率也很高,最好先本身嘗試,本文只是單純的記錄一下本身總體的思路;算法

  • 在閱讀本文章以前,最好先解鎖本題的簡單模式 136.Single Number,這對理解本題有較大的幫助;數組

  • 還有不少細節做者都沒有寫進去,是爲了留給讀者一點思考的空間,實際上是由於懶spa

  • 因爲做者我的水平等緣由,出現錯誤在所不免,還望各位看官海涵。3d

注意 Note 中的第一個條件:The order of the result is not important.,這個條件很是重要,這關係到算法的正確性。code


而後給出整個算法的具體思路,假設數組中兩個不一樣的數字爲 A 和 B;blog

  1. 經過遍歷整個數組並求整個數組全部數字之間的 XOR,根據 XOR 的特性能夠獲得最終的結果爲 AXORB = A XOR B圖片

  2. 經過某種特定的方式,咱們能夠經過 AXORB 獲得在數字 A 和數字 B 的二進制下某一位不相同的位;由於A 和 B 是不相同的,因此他們的二進制數字有且至少有一位是不相同的。咱們將這一位設置爲 1,並將全部的其餘位設置爲 0,咱們假設咱們獲得的這個數字爲 bitFlag;ip

  3. 那麼如今,咱們很容易知道,數字 A 和 數字 B 中必然有一個數字與上 bitFlag 爲 0;由於bitFlag 標誌了數字 A 和數字 B 中的某一位不一樣,那麼在數字 A 和 B 中的這一位必然是一個爲 0,另外一個爲 1;而咱們在 bitFlag 中將其餘位都設置爲 0,那麼該位爲 0 的數字與上 bitFlag 就等於 0,而該位爲 1 的數字與上 bitFlag 就等於 bitFlagleetcode

  4. 如今問題就簡單了,咱們只須要在循環一次數組,將與上 bitFlag 爲 0 的數字進行 XOR 運算,與上 bitFlag 不爲 0 的數組進行獨立的 XOR 運算。那麼最後咱們獲得的這兩個數字就是 A 和 B。

先給出具體實現,引用自 proron's Java bit manipulation solution,我修改了部分代碼以便於理解:

public class Solution {
    public int[] singleNumber(int[] nums) {
        int AXORB = 0;
        for (int num : nums) {
            AXORB ^= num; 
        }
        // pick one bit as flag
        int bitFlag = (AXORB & (~ (AXORB - 1)));
        int[] res = new int[2];
        for (int num : nums) {
            if ((num & bitFlag) == 0) {
                res[0] ^= num;
            } else {
                res[1] ^= num;
            }
        }
        return res;
    }
}

接下來,咱們一行行的解析代碼:

int AXORB = 0;
for(int num: nums){
    AXORB ^= num;
}

這段代碼在 136.Single Number 已經解析過,很容易理解最後獲得的結果:假設數組中不一樣的數字爲 A 和 B,那麼 最後獲得的結果是 A XOR B。

隨後的這一行代碼是整個算法中的難點:

//pick one bit as flag
int bitFlag = (AXORB & (~ (AXORB - 1)));

這一行代碼的做用是:找到數字 A 和數字 B 中不相同的一位,並將該位設置爲 1,其餘位設置爲 0;
根據 XOR 的定義,咱們知道,在 AXORB 中,爲 1 的位即 A 和 B 不相同的位,AXORB 中爲 0 的位即 A 和 B 中相同的位
因此,要找到 A 和 B 中不相同的位,只須要找到在 AXORB 中從右往左第一個爲 1 的位,保留該位並將其餘位置爲 0 便可。

//其實這一行與下面的代碼等價,可是論逼格就差遠了(手動斜眼
public static int f(int num){
    int times = 0;
    while(num > 0){
        if(num % 2 == 1){
            break;
        }
        times++;
        num = num >> 1;
    }
    
    return 1 << times;
}
//下面這個返回 true
System.out.println(Stream.iterate(1, num -> num + 1).limit(Integer.MAX_VALUE).allMatch(num -> f(num)==(num & (~(num -1)))));

咱們能夠把這一行代碼解析爲三步:

int tmp0 = AXORB - 1;
  • 假設 AXORB 從右往左出現的第一位非 0 數字出如今第k位,那麼數字 AXORB 能夠表示爲,可能等於 0:

圖片描述

若是 a0 = 1;那麼問題很是簡單, AXORB - 1 能夠表示爲:

圖片描述

int tmp1 = ~tmp0;

圖片描述

int bitFlag = AXORB & tmp1;

圖片描述

由前面假設咱們知道 a0=1,因此很明顯, bitFlag = 1;

若是 a0 != 1,咱們一樣很容易得出這一行代碼的做用就是將數字 AXORB 的從右到左第一個出現 1 的位置爲 1,其餘位所有置爲 0。其實一點都不容易,只不過用 laTex 寫數學公式好蛋疼啊,因此偷下懶

到這裏,整個算法基本上就沒什麼難點了,你們自行理解吧。

我了個大槽,我原本只是想試試新學的 laTex,結果他喵的就寫了這麼多。大寫加粗的坑

相關文章
相關標籤/搜索