1. 程式人生 > >leetcode260只出現一次的數字

leetcode260只出現一次的數字

leetcode260

題目描述:

一個int陣列中,只有兩個數只出現過一次,其餘的數都出現兩次,求這兩個數

輸入:

一個int陣列

輸出:

一個數組,包含兩個數,這兩個數只出現過一次

要求:

恆定的空間複雜度

思路:

一開始看到這個題感覺應該用HashMap,但是看到要求是使用恆定的空間複雜度明顯HashMap是達不到恆定的空間複雜度的要求的。我也想過用異或,但是異或完後只能得到一個數,就是要求的兩個數的異或值,如何將這兩個數從異或的值中分離出來還是不懂。

然後在論壇看見一個大神的思路,太牛逼了。巧妙的利用了補碼。一個數和一個數的相反數做與操作得到的是這個數從右往左第一個不為0的數。

舉個例子:

名稱 編碼
數字 -5
原碼 10000000_00000000_00000000_00000101
反碼 11111111_11111111_11111111_11111010
補碼 11111111_11111111_11111111_11111011
名稱 編碼
數字 5
原碼,反碼,補碼 00000000_00000000_00000000_00000101

5 & -5 = 00000000_00000000_00000000_00000001

名稱 編碼
數字 -6
原碼 10000000_00000000_00000000_00000110
反碼 11111111_11111111_11111111_11111001
補碼 11111111_11111111_11111111_11111010
名稱 編碼
數字 6
原碼,反碼,補碼 00000000_00000000_00000000_00000110

6 & -6 = 00000000_00000000_00000000_00000010 = 2

A&-A = A原碼的最後一個1代表的數字。

求這個有什麼用呢?

在陣列中所有的數字異或之後,得到的數字是兩個只出現過一次的數字的異或值,因為出現過兩次的數字,就是自身異或自身得0,所以異或完後得到的就是兩個要求的數的異或值。

其次,這個1代表什麼呢?代表這兩個要求的數在這一位不同,因為異或是相同為0,不同為1,所以既然求得了1,所以兩個數在這一位上一定不相同,那麼這樣就好求了,首先對於多有的數異或,求得結果後與自己的相反數做與操作得到res。然後對於陣列中多有的數,如果 num&resInt == 1 說明該數在這一位為1。用一個數res[0]不斷相與,最後得到一個數就是要求的數之一。 如果 num&resInt == 0 說明這些數該位為0,用另一個數res[1]不斷異或這些數,得到的就是另一個我們要求的數。

因為那些出現過兩次的數不管歸類到該位為0還是為1,在異或兩次後都為0,所以最終結果就是要求的兩個數

程式碼:

    public int[] singleNumber(int[] nums) {
        int[] res = new int[2];
        int resInt = 0;
        for (int num : nums) {
            resInt ^= num;
        }
        resInt &= -resInt;
        for (int num : nums) {
            if ((resInt & num) == 0) {
                res[0] ^= num;
            }
            else {
                res[1] ^= num;
            }
        }
        System.out.println(Arrays.toString(res));
        return res;
    }

總結:

  • A&-A = A原碼的最後一個1代表的數字。
  • 巧妙利用補碼,反碼,原始碼,與,或,異或操作。

zsjwish

2018年4月22日19:08:11