問題描述:
一個整型數組 nums
裏除兩個數字之外,其他數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。
示例 1:
輸入:nums = [4,1,4,6]
輸出:[1,6] 或 [6,1]
示例 2:
輸入:nums = [1,2,10,4,1,4,3,3]
輸出:[2,10] 或 [10,2]
解題思路:
若不考慮時間空間複雜度,建立map鍵值對,統計字符個數,如下:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
map<int,int> m;
//統計個數
for(int i=0;i<nums.size();i++){
m[nums[i]]++;
}
vector<int> res;
//遍歷查找
map<int, int>::iterator iter;
for(iter = m.begin(); iter != m.end(); iter++) {
if(iter->second==1) res.push_back(iter->first);
}
return res;
}
};
可是題目要求,時間複雜度O(n),空間複雜度O(1)
於是上面的思路不行,轉換思路,這題其實我最開始寫的時候只有一半思路
就是使用^運算符,異或運算符,是相同取0,不同取1
例如
A = 0011 1100
B = 0000 1101
A^B = 0011 0001
同時還有與&,或|運算符
A&B = 0000 1100
A|B = 0011 1101
假設問題1:
如果數組中只存在一個單獨出現一次的元素,其他元素都出現倆次,nums={a,a1,a1,...,an,an},類似這種形式,求出現一次的元素值
數組中的全部元素進行異或運算
res = a^a1^a1^...^an^an
已知ai^ai=0 則res = a即爲出現一次的元素
此問題就是LeetCode 136題,感興趣的可以看看LeetCode 136
但是題意已給出數組中,只有倆個數字出現一次,其餘都出現了倆次,nums={a,a1,a1,b,...,an,an}就是這種形式
首先將數組中的全部元素進行異或運算
res = a^a1^a1^b^...^an^an
已知ai^ai=0,則res=a^b,則我們已經求出res爲倆個單獨元素的異或值
當時做到這裏,我就卡殼了,後來看了大神的解法
既然a,b 不一樣,那麼res肯定不是0,那麼res的二進制肯定至少有1位(第n位)是1,只有0^1纔等於1
所以a,b 在第n位,要麼a是0,b是1 ,要麼b是0,a是1
A = 0011 1100
B = 0000 1101
A^B = 0011 0001 如此例子,B在第一位是1,A在第一位是0。
在計算機中 數字都是以補碼形式存在,正數補碼等於自己,負數的補碼等於反碼+1,反碼是符號位不變,其他位取反
s = 3 ^ 5; 0011 ^ 0101 = 0110 = 6假設int是8位
-6 原碼1000 0110
反碼1111 1001
補碼1111 1010
下面的建議大家牢記,除非你對計算機基礎掌握很熟練
s&(-s) = 0000 0010
令k = s&(-s),則k就是就是保留s的最後一個1(從右往左),並且將其他位變爲0 也就是s最後一個1是倒數第二位
k&3 = 0010,k&5=0000
此時我們就可以用k來區分倆個單獨數字,這個時候我們考慮數組裏面其他成對出現的元素,
值分爲倆類,k&a=0 或者 k&a!=0
到這裏大家就發現了,使用k將此問題分解爲倆個上面所描述的問題1,具體代碼如下:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int ans = 0;
for(int i=0;i<nums.size();i++){
ans = ans^nums[i];
}
int k = (-ans)&ans;
vector<int> res(2,0);
for(int i=0;i<nums.size();i++){
if(k&nums[i]){
res[0]=res[0]^nums[i];
}else{
res[1]=res[1]^nums[i];
}
}
return res;
}
};