leetcode-260.只出现一次的数字 III

位运算


题目详情

给定一个整数数组 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};
    }
};

位运算常用技巧

位运算常用技巧

posted @ 2022-08-05 17:35  ggaoda  阅读(4)  评论(0编辑  收藏  举报  来源