位运算相关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;
}
};
本题要点
-
异或的性质:
- 任何数和自身异或的结果都是0(异或是不相同才为1)
- 任何数和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,相当于标记了进位的位置,左移一位,将这些进位放到相加过程中正确的位置)

浙公网安备 33010602011771号