解锁算法效率密码!今日系统解析位运算的底层原理与实战技巧,涵盖状态压缩、位掩码、位运算优化等场景,结合大厂高频真题,彻底掌握O(1)空间复杂度的优化利器。
一、位运算核心操作符
运算符 | 符号 | 功能说明 | 应用场景 |
---|---|---|---|
与 | & | 两数对应位均为1则1 | 掩码操作、取特定位 |
或 | | | 两数对应位有1则1 | 合并标记位 |
异或 | ^ | 两数对应位不同则1 | 数值交换、消除重复项 |
取反 | ~ | 按位取反 | 配合掩码使用 |
左移 | << | 所有位左移,低位补0 | 快速乘2 |
右移 | >> | 所有位右移,高位补符号位 | 快速除2(算术右移) |
二、六大高频应用场景
场景1:快速判断奇偶性
bool isOdd(int n) {
return n & 1; // 奇数返回1,偶数返回0
}
场景2:交换两数(无需临时变量)
void swap(int &a, int &b) {
a ^= b; // a = a^b
b ^= a; // b = b^(a^b) = a
a ^= b; // a = (a^b)^a = b
}
场景3:计算二进制中1的个数(汉明重量)
int hammingWeight(uint32_t n) {
int count = 0;
while (n) {
n &= (n - 1); // 消除最右侧的1
count++;
}
return count;
}
场景4:状态压缩(N皇后问题优化)
int totalNQueens(int n) {
int count = 0;
function<void(int, int, int, int)> dfs = [&](int row, int cols, int diag1, int diag2) {
if (row == n) { count++; return; }
int bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1); // 可放置位置
while (bits) {
int pick = bits & -bits; // 取最低位的1
dfs(row+1, cols | pick, (diag1 | pick) << 1, (diag2 | pick) >> 1);
bits &= bits - 1; // 移除最低位的1
}
};
dfs(0, 0, 0, 0);
return count;
}
场景5:快速幂运算(模运算优化)
long long quickPow(long long a, int b, int mod) {
long long res = 1;
while (b) {
if (b & 1) res = (res * a) % mod; // 当前位为1时累积
a = (a * a) % mod; // 平方倍增
b >>= 1; // 右移一位
}
return res;
}
场景6:只出现一次的数字(LeetCode 136)
int singleNumber(vector<int>& nums) {
int res = 0;
for (int num : nums) res ^= num; // 异或消除重复项
return res;
}
三、大厂真题实战
真题1:二进制求和
题目描述:
给定两个二进制字符串,返回它们的和(二进制表示)
位运算解法:
string addBinary(string a, string b) {
int i = a.size()-1, j = b.size()-1, carry = 0;
string res;
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) sum += a[i--] - '0';
if (j >= 0) sum += b[j--] - '0';
res.push_back((sum & 1) + '0'); // 取当前位
carry = sum >> 1; // 计算进位
}
reverse(res.begin(), res.end());
return res;
}
真题2:子集生成
题目描述:
给定不含重复元素的整数数组,返回所有可能的子集
位掩码解法:
vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> res;
for (int mask = 0; mask < (1 << n); ++mask) {
vector<int> subset;
for (int i = 0; i < n; ++i)
if (mask & (1 << i))
subset.push_back(nums[i]);
res.push_back(subset);
}
return res;
}
四、位运算优化技巧
技巧名称 | 实现方式 | 应用场景 |
---|---|---|
消除最低位1 | n & (n - 1) | 计算二进制1的个数 |
取最低位1 | n & -n | 状态压缩枚举 |
奇偶性判断 | n & 1 | 快速判断奇偶 |
绝对值计算 | (n ^ (n >> 31)) - (n >> 31) | 避免分支判断 |
大小写转换 | c ^= 32 | ASCII字符转换 |
五、常见误区与避坑指南
1.运算符优先级:位运算符优先级低于比较运算符,需加括号
if (n & 1 == 0) → 错误!应写为 if ((n & 1) == 0)
2. 位移溢出:左移超过位数导致未定义行为(如1 << 32
在32位系统中)
3. 符号位处理:右移运算的符号位扩展问题(使用无符号类型避免)
4. 类型转换陷阱:混合类型运算导致意外结果(如~0
在char类型为-1)
5. 循环终止条件:负数右移可能陷入死循环(应使用无符号类型)
六、总结与扩展
核心优势:
-
将时间复杂度优化至O(1)空间复杂度
-
利用位级并行性提升计算效率
-
适用于状态压缩、哈希优化等场景
扩展思考:
-
如何用位运算实现浮点数快速乘除?
-
如何设计基于位运算的布隆过滤器?
-
位运算在密码学中的典型应用有哪些?
LeetCode真题训练: