leetcode 260. Single Number III 解題思路

題目地址:https://leetcode.com/problems/single-number-iii/description/

題目的原意大致就是 有一個數組,裏面的數字都是成對出現的,只有兩個元素是隻出現了一次,要求我們把這兩個數字找出來。

這題一個效率比較高的解法是利用位運算來解

我們可以先把題目簡化,我們假設一個數組,裏面的數字都是成對出現的,但是有且僅有一個數字出現了一次,那我們怎麼把這個只出現了一次的數字找出來呢?

我們需要利用抑或的兩個特點:

    1、任意數與0抑或結果都是他本身

     2、相同的兩個數異或的結果是0

那我們只要將數組裏面的值全部參與一遍異或那最後的結果就是隻出現了一次的數字,代碼如下:

ret = 0
for i in nums:
    ret ^= i
return ret

但是本題裏面是有兩個元素出現了一次,那我們應該怎麼解決呢?其實還是利用異或的兩個特性,我們可以把數組分成兩部分,兩個只出現了一次的數字分別在不同的組,組裏了其他的元素都是成對出現的,那麼問題就迎刃而解了。但是問題是怎麼能讓這兩個元素分在不同的組呢?

首先我們依然要將數組裏面的數異或一遍。

假設數組爲:[1,2,1,3,2,5] 那麼異或以便的結果爲 3^5=6 轉化爲二進制爲110,我們假設整型只佔一個字節,那麼二進制爲00000110,這代表着兩個只出現了一次的數字(3和5)
他們的二進制第二位(從右邊數)跟第三位的值是不同的,其他都是相同的。所以我們只要找到一個不同的位數與兩個數做按位與的運算,必然有一個結果爲0,例如:
我們可以取第二位爲: 10 轉化爲10進制是2,2&5=0, 也可以取第三位爲: 100 轉化爲10進製爲4,4&5=0
但是我們該如何取到有一位不同的值呢,我們可以用補碼的特點,取到最後爲1的值。
我們知道計算機裏面的位運算都是用補碼來做運算的,我們通過上一步的異或得到了結果爲6(00000110),我們只要與其相對應的負數-6(11111010)做按位與的計算就可以得到(00000010)。
6的原碼爲 00000110  反碼爲 00000110 
-6的原碼爲 10000110 反碼爲 11111001
那麼如果6與-6的反碼做運算的話結果爲0,但是計算機裏面的運算都是以補碼來做的,正數的補碼是其本身,負數的補碼爲除符號位各位取反末位+1(反碼+1),
那麼我們只有將異或得到的結果與其相反的數做按位與的運算,就可以得到其取到最後爲1的值,這樣就可以分爲兩組了。代碼如下:

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        xor = 0
        for i in nums:
            xor ^= i
        last_diff = (-xor) & xor
        ret = [0, 0]
        for i in nums:
            if i & last_diff:
                ret[0] ^= i
            else:
                ret[1] ^= i
        return ret

 

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