1. leetcode136
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
要求時間複雜度O(n),空間複雜度O(1)。
示例:輸入[4, 1, 2, 1, 2],輸出4
(1)面試官不想要的答案:建字典、排序。
(2)面試官想要的答案:位運算。
思路:如果我們對 0 和二進制位做 XOR 運算,得到的仍然是這個二進制位,a^0=a
如果我們對相同的二進制位做 XOR 運算,返回的結果是 0,a^a=0
XOR 滿足交換律和結合律,a^b^a=a^a^b=0^b=b(位運算都滿足交換律)
所以我們只需要將所有的數進行 XOR 操作,得到那個唯一的數字。
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
a = 0
for i in nums:
a ^= i
return a
2. leetcode137
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現了三次。找出那個只出現了一次的元素。
示例:輸入: [0,1,0,1,0,1,99],輸出: 99
思路:表面上,上面那題是兩個相等的數取異或得到了0,就剩下了只出現一次的數。實際上是1+1=0也就是二進制運算只取了一位。所以如果要三個數位運算後爲0,就是1+1+1=0,也就是三進制。
從位運算角度看,要求達到:01?01=10,10?01=00,所以可得01^01=10, 10&01=00。
使用a,b分別記錄2位。
b = b xor x & ~a;
a = a xor x & ~b;
但是我並不能直接看出上面的狀態轉移方程。所以,參考評論某大佬的做法,可以使用卡諾圖分析。
a | b | x | new_a | new_b |
0 | 0 | 1 | 0 | 1 |
0 | 1 | 1 | 1 | 0 |
1 | 0 | 1 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
a的卡諾圖:
x\ab | 00 | 01 | 11 | 10 |
0 | 0 | 0 | x | 1 |
1 | 0 | 1 | x | 0 |
b的卡諾圖:
x\ab | 00 | 01 | 11 | 10 |
0 | 0 | 1 | x | 0 |
1 | 1 | 0 | x | 0 |
有:
(~x&a&b)|(~x&a&~b)=~x&a
(x&~a&b)|(x&a&b)=x&b
(~x&~a&b)|(~x&a&b)=~x&b
x&~a&~b
於是有
a=(~x&a)|(x&b)
b=(~x&b)|(x&~a&~b)
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
a=0
b=0
tmp=0
for i in nums:
tmp=a
a=((~i)&a)|(i&b)
b=((~i)&b)|(i&(~tmp)&(~b))
return b
由於代碼的實現中,每一位的計算並不是並行進行的,所以可以通過畫一個行列由a,new_b,x構成的卡諾圖來利用先生成的位。
得到b=(~x&b)|(x&a)
只出現一次數字通解:
統計所有數字中每個位中1出現的總數,那麼對於某個位,1出現的次數一定是3的倍數或者是1或者0,那麼對這個數%3得到的結果就是目的數字在該位上的值。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for (int i = 0; i < 32; i++) { // 確定每一位
int sum = 0;
for (int num : nums) {
sum += (num >> i) & 1; // 統計當前位置有多少個元素
}
ans ^= (sum % 3) << i; // 還原到第i位
}
return ans;
}
};
3. leetcode260
給定一個整數數組 nums
,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。 找出只出現一次的那兩個元素。
示例:
把原數組分成兩組,只出現過一次的兩個數字分別在兩組裏邊,那麼問題就轉換成之前的老問題了,只需要這兩組裏的數字各自異或,答案就出來了。
那麼通過什麼把數組分成兩組呢?
放眼到二進制,我們要找的這兩個數字是不同的,所以它倆至少有一位是不同的,所以我們可以根據這一位,把數組分成這一位都是 1 的一類和這一位都是 0 的一類,這樣就把這兩個數分到兩組裏了。
那麼怎麼知道那兩個數字哪一位不同呢?
回到我們異或的結果,如果把數組中的所有數字異或,最後異或的結果,其實就是我們要找的兩個數字的異或。而異或結果如果某一位是 1,也就意味着當前位兩個數字一個是 1 ,一個是 0,也就找到了不同的一位。
思路就是上邊的了,然後再考慮代碼怎麼寫。