位运算
题目详情
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例2:
输入:nums = [-1,0]
输出:[-1,0]
示例3:
输入:nums = [0,1]
输出:[1,0]
方法一 哈希
利用哈希map存储每个数字的次数,最后统计即可
我的代码:
class Solution
{
public:
vector<int> singleNumber(vector<int>& nums)
{
unordered_map<int, int> map;
for (int num : nums)
{
++map[num];
}
vector<int> ans;
for (const auto& [num, cishu]: map)
{
if (cishu == 1)
{
ans.push_back(num);
}
}
return ans;
}
};
方法二 简化问题 异或运算
在 leetcode-136.只出现一次的数字 中,只有一个不同的数字,那么我们通过异或的方法最后留下来的就是答案,那么这道题如果我们仍然异或下来,那么最后留下的是两个答案的异或值(假设答案是a,b,最后留下的就是a ^ b),那么该咋办呢?哎呀!如果我们可以把a和b分开到两个数组里分别处理就好了
我们可以知道,a ^ b就是将a和b的二进制每一位异或运算,当二者相同时就会得到0,否则就是1
则我们可以像leetcode-136.只出现一次的数字 一样,先将所有数字异或运算,最后得到a ^ b
然后找到这个结果的二进制中的某一个是1的位置,这个点就是分离a和b的关键
我们找到这个位置之后,将原来的所有数中该位置是0的分为一组,该位置是1的分为一组
我们再分别利用异或处理这两组,即可得到答案
我们可以很方便的用x & -x 得到倒数第一位是1的数,但是要注意溢出问题
因为若x = MIN_INT = -2147483648 则-x就是2147483648就会溢出(正数的最大值是2147483647)
我们需要做些处理
class Solution
{
public:
vector<int> singleNumber(vector<int>& nums)
{
int allxorsum = 0;
for (int num : nums) // 将所有数异或
{
allxorsum ^= num;
}
// 防止溢出
int lsb = (allxorsum == INT_MIN ? allxorsum : allxorsum & (-allxorsum));
int ty1 = 0, ty2 = 0;
for (int num : nums)
{
if (num & lsb) // 对应位是1的归为一类进行异或运算
ty1 ^= num;
else // 对应位是0的归为一类进行异或运算
ty2 ^= num;
}
return {ty1, ty2};
}
};