位运算相关leetcode题目学习笔记

位运算相关leetcode题目学习笔记

参考视频链接

一、leetcode191 位一的个数

题目链接

思路分析

  • 当一个数的二进制被减1时,它最低位的1会变成0,且它后面的0都会变成1,将减1之后得到的数和原数进行按位与操作,将会消去最低的这个1。不断的对原数进行消去操作,知道原数变为0,记录消去操作的次数,即为位1的个数。

代码

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        while (n) {
            n &= (n - 1);
            count ++;
        }
        return count;
    }
};

本题要点

  • 任何数减一,其二进制表示中最低的一个1会变成0,更低的那些位会变为1。然后再和自身原来的值相与之后,相当于消除了那个最低的1。可以通过这种方法巧妙的统计二进制位中1的个数

二、面试题 16.01 交换数字

题目链接

思路分析

使用到异或的性质:相同的数异或为0,任何数和0的异或都是这个数本身。另外异或满足交换律和结合律,那么有如下式子:

/* a = original a ^ original b
* b = b ^ a = original b ^ original a ^ original b = original a
* a = a ^ b = original a ^ original b ^ original a = original b
*/
a = a ^ b;
b = b ^ a;
a = a ^ b;

代码

class Solution {
public:
    vector<int> swapNumbers(vector<int>& numbers) {
        numbers[0] ^= numbers[1];
        numbers[1] ^= numbers[0]; 
        numbers[0] ^= numbers[1];
        return numbers;
    }
};

本题要点

  • 直接背把,其实是异或的交换律和结合律的巧妙应用

三、leetcode136 只出现一次的数字(一次和两次版本)

题目链接

思路分析

  • 考虑异或的性质,任何数和自身异或结果为0,任何数和0的异或都为自身。那么只需要把数组中所有的数进行异或,最终的结果就是只出现了一次的那个数。

代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int result = 0;
        for (int i = 0; i < nums.size(); i ++) {
            result ^= nums[i];
        }
        return result;
    }
};

本题要点

  • 异或的性质:

    1. 任何数和自身异或的结果都是0(异或是不相同才为1)
    2. 任何数和0异或的结果都是自身(如果是0,和0异或是0;如果是1,和0异或是1)

四、leetcode693 交替位二进制数

题目链接

思路分析

  • 一旦出现00或者11,说明不满足。那么使用11来按位与该数的最低两位,并不断右移此数,判断完整个数即可。

代码

class Solution {
public:
    bool hasAlternatingBits(int n) {
        while (n) {
            int m = n % 4;
            if ((m & 3) == 3 || (m & 3) == 0) {
                return false;
            }
            n = n >> 1;
        }
        return true;
    }
};

本题要点

  • 使用1及位与运算的性质(1和任何位与均为该位自身)来获取数的二进制的某些位

五、leetcode 1863 找出所有子集的异或总和再求和

题目链接

思路分析

  • 考虑二进制的特性,一个数组中每个元素存在两种状态:选或者不选,使用二进制枚举来表示每一位的选或者不选,进而枚举数组所有子集的情况

  • 例,对于数组 {1, 3, 7} ,长度为3。 二进制枚举001表示:1不选3不选7选;二进制枚举101表示:1选3不选7选。那么只需要从二进制枚举1遍历到二进制枚举pow(2, 3) - 1,判断这些枚举中1出现的位置并计算异或,累加到结果中即可。如何快速判断二进制中1的出现呢?

  • 答案是利用如下二进制算式。下式的值表示:a的二进制中第b+1位是否为1

a & (1 << b)

代码

class Solution {
public:
    int subsetXORSum(vector<int>& nums) {
        int size = nums.size();
        int result = 0;

        for (int i = 1; i < pow(2, size); i ++) { // 循环二进制枚举
            int tmp = 0;
            for (int j = 0; j < size; j ++) { // 判断子集合的元素
                if (i & (1 << j)) { // 等于1说明该位为1,需要累加结果
                    tmp ^= nums[j];
                }
            }
            result += tmp;
        }
        return result;
    }
};

本题要点

  • 二进制枚举数组子集枚举之间的对应关系
  • a & (1 << b) 巧妙判断数的某一位是否为1

六、leetcode371 两整数之和

题目链接

思路分析

  • 两个数的异或结果x,相当于是抛弃所有进位的加和结果。两个数的与结果y,可以筛选出加和之后所有进位的位置(只有1和1与才是1),将y左移一位,就将每一个进位放到了正确的加和位置。此时用x+y就得到了a+b的结果。但题目不允许使用+和-符号,不能直接对x和y进行相加。

  • 这里直接放一个结论,在不断将a+b转换为新的子问题x+y的过程中,a或者b总有一个会变为0(但是目前还没有理解为什么一定会有一个变成0,后面再研究研究),此时另一个数就是此题的结果。

代码

class Solution {
public:
    int getSum(int a, int b) {
        if (a && b) return getSum(a ^ b, (unsigned int)(a & b) << 1);
        if (a) return a;
        else return b;
    }
};

本题要点

  • 两数异或相当于两数抛弃所有进位之后的加和

  • 两数按位与并左移一位,结果为两数加和中产生的进位值(两位均为1,与的结果才为1,相当于标记了进位的位置,左移一位,将这些进位放到相加过程中正确的位置)

posted @ 2022-08-13 23:00  LeisureLak  阅读(80)  评论(0)    收藏  举报