位操作在OJ 题目中是一种非常高效的工具,常用于优化时间复杂度和空间复杂度。本文是位操作在 OJ 题目中的主要用法总结,并以 C++ 实现为例。
相关题目:《C++⭐算法OJ⭐Single Number 系列(位操作)》
文章目录
- 1. 基本位操作
- (1) 与运算 (`&`)
- (2) 或运算 (`|`)
- (3) 异或运算 (`^`)
- (4) 取反运算 (`~`)
- (5) 左移 (`<<`) 和右移 (`>>`)
- 2. 常见应用场景
- (1) 判断奇偶性
- (2) 交换两个数
- (3) 统计二进制中 1 的个数
- (4) 判断一个数是否是 2 的幂
- (5) 找到最低位的 1
- (6) 快速乘除 2 的幂
- 3. 高级应用
- (1) 状态压缩
- (2) 枚举子集
- (3) 快速幂算法
- (4) 查找只出现一次的数字
- (5) 查找两个只出现一次的数字
- 4. 总结
1. 基本位操作
(1) 与运算 (&
)
用于提取某些二进制位。
示例:提取最低 4 位:
int num = 0b11011010;
int lower4 = num & 0b1111; // 结果为 0b1010
(2) 或运算 (|
)
用于设置某些二进制位。
示例:设置第 3 位为 1:
int num = 0b11011010;
num = num | (1 << 2); // 结果为 0b11011110
(3) 异或运算 (^
)
用于翻转某些二进制位。
示例:翻转第 4 位:
Copy
int num = 0b11011010;
num = num ^ (1 << 3); // 结果为 0b11010010
(4) 取反运算 (~
)
用于翻转所有二进制位。
示例:
int num = 0b11011010;
num = ~num; // 结果为 0b00100101(假设 int 为 8 位)
(5) 左移 (<<
) 和右移 (>>
)
左移:将二进制位向左移动,低位补 0。
右移:将二进制位向右移动,高位补 0(逻辑右移)或补符号位(算术右移)。
示例:
int num = 0b11011010;
int leftShifted = num << 2; // 结果为 0b1101101000
int rightShifted = num >> 2; // 结果为 0b00110110
2. 常见应用场景
(1) 判断奇偶性
利用最低位是否为 1 来判断奇偶性。
示例:
bool isOdd(int num) {
return num & 1; // 如果结果为 1,则是奇数
}
(2) 交换两个数
利用异或运算交换两个数,无需额外空间。
示例:
void swap(int &a, int &b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
(3) 统计二进制中 1 的个数
利用 n & (n - 1)
每次消除最低位的 1。
示例:
int countOnes(int num) {
int count = 0;
while (num) {
num = num & (num - 1); // 消除最低位的 1
count++;
}
return count;
}
(4) 判断一个数是否是 2 的幂
2 的幂的二进制表示中只有一个 1。
示例:
bool isPowerOfTwo(int num) {
return num > 0 && (num & (num - 1)) == 0;
}
(5) 找到最低位的 1
利用 n & -n
可以找到最低位的 1。
示例:
int lowestBit(int num) {
return num & -num;
}
(6) 快速乘除 2 的幂
左移 1 位相当于乘以 2,右移 1 位相当于除以 2。
示例:
int multiplyByTwo(int num) {
return num << 1;
}
int divideByTwo(int num) {
return num >> 1;
}
3. 高级应用
(1) 状态压缩
利用二进制位表示状态,常用于动态规划或回溯算法。
示例:表示集合的子集:
int subset = 0b1010; // 表示集合 {1, 3}
(2) 枚举子集
利用位运算枚举集合的所有子集。
示例:
void enumerateSubsets(int set) {
for (int subset = set; subset; subset = (subset - 1) & set) {
// 处理子集
}
}
(3) 快速幂算法
利用位运算加速幂运算。
示例:
int fastPow(int base, int exponent) {
int result = 1;
while (exponent) {
if (exponent & 1) result *= base;
base *= base;
exponent >>= 1;
}
return result;
}
(4) 查找只出现一次的数字
利用异或运算找出数组中只出现一次的数字。
示例:
int singleNumber(vector<int>& nums) {
int result = 0;
for (int num : nums) result ^= num;
return result;
}
(5) 查找两个只出现一次的数字
利用异或运算和分组思想。
示例:
vector<int> singleNumber(vector<int>& nums) {
int xorResult = 0;
for (int num : nums) xorResult ^= num;
int mask = 1;
while ((xorResult & mask) == 0) mask <<= 1;
int num1 = 0, num2 = 0;
for (int num : nums) {
if (num & mask) num1 ^= num;
else num2 ^= num;
}
return {num1, num2};
}
4. 总结
位操作在 OJ 题目中的主要用途包括:
-
高效计算:如快速幂、统计 1 的个数。
-
状态表示:如状态压缩、枚举子集。
-
优化空间:如交换两个数、判断奇偶性。
-
解决特定问题:如查找只出现一次的数字。