位运算应用
2.2.1 与应用
规则:两个位都是1时结果为1,否则为0
C++
#include <iostream>
using namespace std;
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int res = a & b; // 0101 & 0011 = 0001
printf("a & b = %d\n",res);
// 输出: 5 & 3 = 1
return 0;
}
应用:检查奇偶性、掩码操作
C++
#include <iostream>
using namespace std;
int main() {
int x = 13; // 二进制:...00001101
if (x & 1) {
// 如果 x & 1 的结果非零(即为1),条件为真
cout << x << " 是奇数。" << endl;
} else {
cout << x << " 是偶数。" << endl;
}
// 运算过程:
// ...00001101 (x = 13)
// & ...00000001 (1)
// -----------
// ...00000001 (结果为 1,条件为真)
return 0;
}
2.2.2 或应用
规则:两个位有一个是1时结果为1
C++
#include <iostream>
using namespace std;
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int res = a | b; // 0101 | 0011 = 0111
printf("a | b = %d\n",res);
// 输出: 5 | 3 = 7
return 0;
}
2.2.3 异或****应用
规则:两个位不同时结果为1
C++
#include <iostream>
using namespace std;
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int res = a ^ b; // 0101 ^ 0011 = 0110
printf("a ^ b = %d\n",res);
// 输出: 5 ^ 3 = 6
return 0;
}
① 简单的加密操作
原理: 异或有一个神奇的性质:
- 如果
A ^ B = C - 那么
C ^ B = A
就像一个有两个相同钥匙的锁!
C++
#include <iostream>
using namespace std;
int main() {
string m = "Hello"; // 原始信息
char key = 'K'; // 密钥
cout << "原始信息: " << m << endl;
// 加密:每个字符与密钥异或
for (int i = 0; m[i] != '\0'; i++) {
m[i] = m[i] ^ key;
}
cout << "加密后: " << m << endl;
// 解密:再次与同一个密钥异或
for (int i = 0; m[i] != '\0'; i++) {
m[i] = m[i] ^ key;
}
cout << "解密后: " << m << endl;
return 0;
}
② 交换变量(不需临时变量)
- 先把两杯果汁倒在一起混合(a ^ b)
- 从混合果汁中倒回一杯,得到原来的颜色(b = a ^ b)
- 再从剩下的混合果汁中倒回另一杯,得到另一种颜色(a = a ^ b)
C++
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 3;
cout << "交换前: a = " << a << ", b = " << b << endl;
// 异或交换三连
a = a ^ b; // 步骤1:把a和b的信息"混合"
b = a ^ b; // 步骤2:从混合信息中提取原始a,存入b
a = a ^ b; // 步骤3:从混合信息中提取原始b,存入a
cout << "交换后: a = " << a << ", b = " << b << endl;
return 0;
}
/*
交换前: a = 5, b = 3
交换后: a = 3, b = 5
*/
2.2.4 取反应用
规则:所有位取反(0变1,1变0)
取反后的二进制以「1」开头(符号位为 1),说明是负数。
要得到负数的十进制值,需通过补码反推原码:
补码 → 原码的规则:负数的原码 = 补码的反码 + 1(符号位始终为 1)。
- 二进制: 0000 0000 0000 0000 0000 0000 0000 0101
- 取反: 1111 1111 1111 1111 1111 1111 1111 1010
- 反码: 1000 0000 0000 0000 0000 0000 0000 0101 [ 补码取反(符号位不变)]
- 原码: 1000 0000 0000 0000 0000 0000 0000 0110[ 反码加 1,得到原码 ]
一句话总结取反:
- ~x = - (x + 1)
C++
#include <iostream>
using namespace std;
int main() {
int a = 5; // 二进制: 0000 0000 0000 0000 0000 0000 0000 0101
int res = ~a; // 取反: 1111 1111 1111 1111 1111 1111 1111 1010
cout << "~" << a << " = " << res << endl;
// 套入公式:~x = - (x + 1)
// 输出: ~5 = -6(补码表示)
return 0;
}
2.2.5 左移应用
规则:所有位向左移动,右边补0
C++
#include <iostream>
using namespace std;
int main() {
int a = 5; // 二进制: 0101
int res = a << 2; // 010100 = 20
cout << a << " << 2 = " << res << endl;
// 输出: 5 << 2 = 20
return 0;
}
n << 1 等价于 n * 2
2.1.6 右移应用
规则:所有位向右移动
C++
#include <iostream>
using namespace std;
int main() {
int a = 20; // 二进制: 10100
int res = a >> 2; // 00101 = 5
cout << a << " >> 2 = " << res << endl;
// 输出: 20 >> 2 = 5
return 0;
}
n >> 1 等价于 n / 2
**场景1:**二分查找
// 传统写法
int mid = (l+ r) / 2;
// 优化写法(防止溢出 + 性能更好)
int mid = l+ ((r- l) >> 1);
场景2:快速计算平均值
// 计算两个数的平均值
int avg= (a + b) >> 1;
// 比 (a + b) / 2 更快
场景3:循环减半
// 快速将数字减半直到0
while (n > 0) { cout << n << " "; n = n >> 1; // 快速除以2
}
【位运算技巧】
操作功能形象比喻n & (n-1)移除最低位的1摘果子n & (-n)保留最低位的1,其他全清0独留最右边的果子
举例子:n = 12 (二进制 1100)
n & (n-1)****:移除最低位的1
C++
n: 1 1 0 0 (12)
n-1: 1 0 1 1 (11)
----------- &
结果: 1 0 0 0 (8) ← 移除了第2位的1
n & (-n)****:保留最低位的1
C++
n: 1 1 0 0 (12)
-n: 0 1 0 0 (-12,省略含符号的前4个1)
----------- &
结果: 0 1 0 0 (4) ← 只保留了第2位的1
扩展:其他移位运算的数学意义
运算数学意义例子n << 1n * 25 << 1 = 10n << 2n * 45 << 2 = 20n << kn * 2^k3 << 3 = 24n >> 1n / 210 >> 1 = 5n >> kn / 2^k16 >> 2 = 4
口诀:左移翻倍,右移减半, 移位运算,速度飞快