《劍指offer》:數組中數字出現的次數--分組異或

0x01.問題

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

示例 1:

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

示例 2:

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

public int[] singleNumbers(int[] nums) 

0x02.分析思路

抓住問題的關鍵字眼:

  • 除兩個數其它數全部存在兩遍
  • 要求只能只能使用常數級別的空間

空間是常數級別,說明不能使用哈希表來存儲已經出現過的數字,那我們如果需要找出不同的那一個數字怎麼辦呢?
答案是異或,我們知道,任何數和自身的異或爲0,也就說,如果數組中只存在一個數字不同的話,對整個數組進行異或,最終相同數的異或結果爲0,剩下的值就是那個不同的數的值了。

問題是,這個問題存在了兩個不同的數字,怎麼辦呢?
如果數組中存在兩個不同的數字,那麼對整個數組進行異或的結果應該是num1^num2,也就是那兩個不同數的異或得到的值,而異或不可逆,所以,這樣,我們就不能得到這兩個數字的具體值。

既然對整個數組異或得到的是那兩個數的異或結果,如果,我們能夠將這個數組分爲兩個部分,一個部分包含一些相同的數字和第一個不同的數,一個部分包含一些相同的數字和第二個不同的數,然後再分別對兩個部分進行異或,這樣,是不是就能夠得到這兩個數了。

那麼,問題來到了,如何去分組,也就是說,根據什麼樣的情況去分組?
我們先來看一下我們分組需要達到的目的是怎樣的:

  • 保證一些相同的數字都在一個組內,保證不同的數字都在不同的組內。

也就是說,我們只要找到一個條件,能夠區分開組內相同的數字和不同的數字就行了。

我們在上面的分析中得到了兩個不同的數最後異或的結果,在這個結果中,兩個數相同的位上得到的是0,位上不相同則得到的數是1,如果我們能夠將一個爲1的位單獨拿出來,那麼,這兩個數對應的位與這個位進行&運算的值一定不同,根據這樣一個條件,我們就能夠將兩個數劃分出來,那麼,還有一個問題,怎樣才能保證相同的數在一個組內呢?如果兩個數相同,那麼對應的位應該是完全一樣的,得到的結果也是一樣的,所以進行&運算得到的結果一定是一樣的。

這樣,我們就可以根據一個這樣的位來將這個數組劃分爲兩個部分。

整體算法思路:

  • 對整個數組進行異或,得到結果xor
  • xor的一個爲1的位flag
  • 進行分組判斷,如果flag&num==0,分入第一組,否則分入第二組,對兩組分別異或,得到最終的結果。

0x03.解決代碼–分組異或

class Solution {
    public int[] singleNumbers(int[] nums) {
        int xor=0;
        for(int num:nums){
            xor^=num;
        }
        int flag=1;
        while((flag&xor)==0){
            flag<<=1;
        }
        int group1=0,group2=0;
        for(int num:nums){
            if((num&flag)==0){
                group1^=num;
            }else{
                group2^=num;
            }
        }
        return new int[]{group1,group2};
    }
}

ATFWUS --Writing By 2020–04-28

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章