Leetcode 260:只出現一次的數字 III(最詳細的解法!!!)
阿新 • • 發佈:2018-11-11
給定一個整數陣列 nums
,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。 找出只出現一次的那兩個元素。
示例 :
輸入: [1,2,1,3,2,5]
輸出: [3,5]
注意:
- 結果輸出的順序並不重要,對於上面的例子,
[5, 3]
也是正確答案。 - 你的演算法應該具有線性時間複雜度。你能否僅使用常數空間複雜度來實現?
解題思路
類似問題
Leetcode 136:只出現一次的數字(最詳細的解法!!!)
Leetcode 137:只出現一次的數字 II(最詳細的解法!!!)
首先想到的解法就是像137
問題一開始提出的那樣,使用dict
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums_dict = {}
for num in nums:
nums_dict[num] = nums_dict.get(num, 0) + 1
result = list()
for key, val in nums_dict.items():
if val == 1:
result.append(key)
return result
但是如果使用O(1)
的空間複雜度要怎麼做呢?我們很快想到通過位運算。一個非常簡單的思路就是我們按照136
問題中的方法,直接對每個nums
的元素做xor
,最後我們得到的結果就是兩個單一元素a
和b
的xor
。因為a
和b
不相同,所以它們之間必定會存在至少一個bit
不同,也就是說a xor b
的結果中至少有一個bit
是1
。我們從這麼多的bit
中挑選出一個,然後其餘位置為0
,那我們就構成了這樣的一種mask
。例如
00...100
這樣的mask
a
和b
中元素與元素的話,必定有一個結果是0
,另外一個結果是mask
,這樣我們就將a
和b
給分開了。那麼問題就變成了,怎麼構建這樣的mask
?我們使用一個簡單的策略就是a xor b
的最右邊的1
作為flag
。那要怎麼得到最右邊的1
呢?這就涉及到補碼的概念,我們知道負數在計算機中使用補碼錶示的,也就是反碼加1
num:5
原碼:0101
反碼:1010
---------
num:-5
補碼:1011
那麼我們通過num&-num
就可以取出最右邊的1
了。現在我們就可以遍歷nums
然後,通過mask
就可以判斷nums
中的那些元素的右邊第一位是1
(根據上面例子),我們將這些數分成一類,將右邊第一位是1
的數分成為另外一類,並且我們的a
和b
也就被分到不同的組中。這兩組數字的個數不一定相同,但是最後一定是可以相互通過xor
消除,最後只剩a
和b
。
from functools import reduce
from operator import xor
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
mask = reduce(xor, nums)
mask &= -mask
result = [0]*2
for num in nums:
if num & mask:
result[0] ^= num
else:
result[1] ^= num
return result
我將該問題的其他語言版本新增到了我的GitHub Leetcode
如有問題,希望大家指出!!!