[LC优选算法#12] 位运算 | 两整数之和 | 只出现一次的数字 II | 消失的两个数字

1. 两整数之和

两整数之和

解题思路:

不能使用+和-是一大痛点,但是如果真的没思路,笔试时直接使用也是可以对(bushi)

普通的整数运算符这条路是行不通了,但是有没有其他的运算符可以使用呢?有的有的。我们可以把加数转化为二进制,使用位运算解决。

  1. 位运算 O(1):我们可以使用异或^和与&运算达到加法运算符的效果。
    根据这两个运算符的规则,我们可以把异或 看作无进位相加 看作进位不相加

这样,我们就有基本思路了:

一直循环无进位加法和进位,直至没有进位为止。

cpp 复制代码
class Solution {
public:
    int getSum(int a, int b)
    {
        while(b != 0)
        {
            unsigned int sum = a ^ b;
            unsigned int carry = a & b; 
            //转为无符号,防止int左移时,最高位符号处理时发生未定义情况
            
            a = sum;
            b = (carry << 1);
        }

        return a; //自动强转
    }
};

2. 只出现一次的数字 II

只出现一次的数字 II

这道题中需要找到只出现一次的数字,数组中的其他数字都出现了三次。

解题思路:

可以使用哈希表解决,但空间复杂度不是最优的。那有没有不需要开空间的解法呢?有的有的,我们可以通过位运算解决此类问题。

  1. 位运算 O(N):设要找的数为ret,我们将数组中所有数的每个比特位 相加,然后**%3**,这样就可以快速定位到ret的每个比特位是0还是1了;组合每一个比特位,即可得到ret

这种方法可以处理数组中某个数出现一次,其余数出现n次的类似题。

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums)
    {
        int ret = 0;

        for(int i=0; i<32; i++)
        {
            int sum = 0;

            for(auto x : nums)
            {
                sum += ((x >> i) & 1);
            }

            sum %= 3;
            if(sum == 1)
            {
                ret |= (1 << i); //将某个二进制位设置为1
            }
        }

        return ret;
    }
};

3. 消失的两个数字

消失的两个数字

在上一篇文章中,我们了解了如何在0-n中寻找缺失的某个数字,方法是把数组中的数和0-n全部异或在一起:

LC优选算法#11 位运算 | 判断字符是否唯一 | 丢失的数字

略微不同的是,本题是 268. 丢失的数字 + 260. 只出现⼀次的数字 III 组合起来的题。

丢失的数字

只出现⼀次的数字 III

在本题我们需要找出数组中缺失的两个数字,如果使用位运算的方法,仅仅全部异或的方法是不奏效的。怎么办呢?

解题思路:

  1. 位运算 O(N):我们可以先将数组中的数和1~n区间内的所有数异或 在⼀起,问题就变成了:有两个数出现了⼀次,其余所有的数出现了两次。进⽽变成了260这道题。

现在的关键就在于:如何将这两个数分在不同组 ?我们可以根据全部异或的结果,找到比特位为1 的位置(相异为1,说明缺失的两个数在这一个比特位上一定不同),根据这个位置的不同,将数组划分为此位为1此位为0两类,分别进行异或,这样得出的结果就是缺失的数了。

cpp 复制代码
class Solution {
public:
    vector<int> missingTwo(vector<int>& nums)
    {
        //异或nums和1~N的数
        int n = nums.size();
        int val = 0;
        for(auto x : nums)
        {
            val ^= x;
        }

        for(int i=1; i<=n+2; i++)
        {
            val ^= i;
        }

        //找不同位
        int pos = val & (-val);
        int a = 0;
        for(auto x : nums)
        {
            if((x & pos) != 0)
            {
                a ^= x;
            }
        }

        for(int i=1; i<=n+2; i++)
        {
            if((i & pos) != 0)
            {
                a ^= i;
            }
        }

        val ^= a;

        return {val, a};
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~