理解位运算的原理与应用
位运算是对整数的二进制位进行操作的运算。与普通算术运算不同,位运算直接在二进制级别上操作,效率极高。
| 运算符 | 名称 | 符号 | 描述 | 示例 |
|---|---|---|---|---|
| AND | 按位与 | & | 两个位都为 1 时结果为 1 | 5 & 3 = 1 |
| OR | 按位或 | | | 两个位任意一个为 1 时结果为 1 | 5 | 3 = 7 |
| XOR | 按位异或 | ^ | 两个位不同时结果为 1 | 5 ^ 3 = 6 |
| NOT | 按位取反 | ~ | 将每个位取反(0→1,1→0) | ~5 = -6 |
| LEFT | 左移 | << | 将所有位向左移动 n 位 | 5 << 1 = 10 |
| RIGHT | 右移 | >> | 将所有位向右移动 n 位 | 5 >> 1 = 2 |
| A | B | A & B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
if (x & mask) { ... }x = x & ~mask;x & 1 == 0 为偶数x % 8 等价于 x & 7| A | B | A | B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
x = x | mask;flags |= READ | WRITE;| A | B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
A ^ 0 = A(与 0 异或保持不变)A ^ A = 0(自己与自己异或得 0)A ^ B ^ A = B(可逆性)A ^ B ^ C = A ^ (B ^ C)(满足交换律和结合律)a ^= b; b ^= a; a ^= b;x ^= mask;encrypted = data ^ key;~5 在计算机中不是 -6 的原码,而是 -6 的补码!
~mask 用于清零除指定位外的所有位-x = ~x + 1将所有位向左移动,右边补 0
将所有位向右移动,左边补符号位(算术右移)或 0(逻辑右移)
x << 3 比 x * 8 快(x >> n) & mask// 传统方法
bool isOdd(int x) {
return x % 2 != 0;
}
// 位运算方法(更快)
bool isOdd(int x) {
return x & 1;
}
原理:最低位为 1 则是奇数
// 不使用临时变量
void swap(int &a, int &b) {
a ^= b;
b ^= a;
a ^= b;
}
// 验证:
// a = 5, b = 3
// a = 5 ^ 3 = 6
// b = 6 ^ 5 = 3
// a = 6 ^ 3 = 5
// 数组中只有一个数出现一次
int findUnique(vector<int> nums) {
int result = 0;
for (int x : nums) {
result ^= x;
}
return result;
}
原理:x ^ x = 0,成对出现的数抵消
bool isPowerOfTwo(int x) {
return x > 0 && (x & (x - 1)) == 0;
}
原理:2的幂次二进制只有1个1
int hammingDistance(int a, int b) {
int xor = a ^ b;
int count = 0;
while (xor) {
count += xor & 1;
xor >>= 1;
}
return count;
}
两个数二进制不同位的个数
const int READ = 1; // 0001
const int WRITE = 2; // 0010
const int EXECUTE = 4; // 0100
// 设置权限
int permissions = READ | WRITE;
// 检查权限
if (permissions & READ) {
// 有读权限
}
// 移除权限
permissions &= ~WRITE;
1 << 0 = 1 // 2^0
1 << 1 = 2 // 2^1
1 << 2 = 4 // 2^2
1 << n = 2^n // 2^n
int lowestOne = x & -x;
// 例如:x = 12 (1100)
// x & -x = 4 (0100)
int removeLowest = x & (x - 1);
// 例如:x = 12 (1100)
// x & (x-1) = 8 (1000)
int countOnes(int x) {
int count = 0;
while (x) {
x &= (x - 1);
count++;
}
return count;
}
uint32_t reverseBits(uint32_t n) {
uint32_t result = 0;
for (int i = 0; i < 32; i++) {
result = (result << 1) | (n & 1);
n >>= 1;
}
return result;
}