一个整型数组 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结构,利用其find和erase函数来实现,但耗时较长。
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
map<int, bool>room;
vector<int>res;
for (int i = 0; i < nums.size(); ++i)
{
if (room.find(nums[i]) == room.end())
room[nums[i]] = 1;
else
room.erase(nums[i]);
}
for (map<int, bool>::iterator it = room.begin(); it != room.end(); it++)
{
res.push_back(it->first);
}
return res;
}
};
后学习到位运算,通过异或操作,相同的两个数异或之后变为0(1010^1010 = 0000),不同的数异或之后再不同的位处为1,(exa:1010^1011 = 0001),任何数异或0不变(1010^0000 = 1010),同时很多数一起异或仍具有上述性质。因此所有数异或之后相当于只有两个不同数的异或。
再将抑或后的结果a,利用res = a&(-a),得到最后一个1(exa:对于a = 0000 0110,表示1、2位置对应两个不同数的不同位置,
res = a&(-a)=0000 0110 & 1000 0110(原码)=0000 0110 & 1111 1010(补码)=0000 0010,即1位置为1,其余为0)
经过上述我们知道我们要找的两个数在1位置一定不同,一个为1,一个为0,通过将数与res与即能将两个数分在两个区间,在将每个区间的数做异或,最后得到我们要的结果。
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int ans= 0;
vector<int> res(2,0);
for(int num:nums) ans ^= num;
int pos = ans&(-ans); // 找到最低位1出现的位置
for(int num:nums){
if((num & pos) == pos) res[0] ^= num;
else res[1]^=num;
}
return res;
}
};