算法题 只出现一次的数字 C++实现

23 阅读2分钟

只出现一次的数字中的三道算法题,无疑是我们在刚刚学习位运算很好的练习题,尤其其中的第三道,我认为思路非常的精彩。

1. 只出现一次的数字

136. 只出现一次的数字

这里讲一个异或运算的规则:

a ^ b = b ^ a

a ^ a = 0

这道题目,将所有的值异或起来,相同的两个值异或为0,这样就只剩下单独的那个数了

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int x = 0;
        for (int num : nums)  
            x ^= num;
        return x;            
    }
};

2. 只出现一次的数字 II

137. 只出现一次的数字 II

思路:对于数组中的所有数,如果我们只看最后一个bit位,他们只会出现:
3个0 + 出现一个那个数为0  将他 MOD 3 = 0

3个0 + 出现一个那个数为1  将他 MOD 3 = 1

3个1 + 出现一个那个数为0  将他 MOD 3 = 0

3个1 + 出现一个那个数为1  将他 MOD 3 = 1

32个位也是一样的~

 这样就可以推广一下,相同的数出现N次相加上只出现一次的数,最后MOD N 后就是只出现一次的值

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for (int i = 0; i < 32; ++i) {
            int cnt = 0;
            for (auto x : nums) {
                cnt += (x>>i)&1;
            }
            res |= (cnt%3)<<i;
        }
        return res;
    }
};

 

3. 只出现一次的数字 III

 260. 只出现一次的数字 III

直接说思路和难点,

难点:如何在数组中区分出来两个值?全部异或在一起后又如何区分?

思路:将所有的值全部异或在一起,那么就能得到这两个数异或起来的结果。因为异或的结果表示的是二进制位不同的地方。所以我们可以找到第一个不同的结果

​编辑

 通过这个第一个不同的二进制位,将数组中全部的值分为两组,两组分别异或在一起就可以得到这两个值。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
                vector<int> ret = { 0,0 };
        int tmp = 0;
        for (auto x : nums)
        {
            tmp ^= x;
        }
        int pos = 0;
        for (int i = 0; i <= 31; ++ i)
        {
            if ((tmp >> i) & 1)
            {
                pos = i;
                break;
            }
        }
        for (auto x : nums)
        {
            if ((x >> pos) & 1)
                ret[0] ^= x;
            else
                ret[1] ^= x;
        }
        return ret;
    }
};

4. 总结

位运算最常用在嵌入式开发中,熟练掌握位运算,可以在单片机中宛若游龙~