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