在算法竞赛中,二进制运算因其高效性和简洁性被广泛应用于优化时间复杂度、空间复杂度以及代码实现。持续更新中…
一、二进制基础运算规则
1. 基本位运算符
运算符 | 符号 | 定义(按位操作) | 示例(二进制) |
---|---|---|---|
与 | & | 全1为1,否则为0 | 1010 & 1100 = 1000 |
或 | ` | ` | 有1为1,全0为0 |
非 | ~ | 取反(注意补码符号位) | ~1010 = 0101 (假设4位) |
异或 | ^ | 相同为0,不同为1 | 1010 ^ 1100 = 0110 |
2. 移位运算
操作 | 符号 | 定义 | 示例(二进制) |
---|---|---|---|
左移 | << | 低位补0,高位溢出丢弃 | 1010 << 2 = 101000 |
逻辑右移 | >>> | 高位补0,低位溢出丢弃(无符号数) | 1010 >>> 2 = 0010 |
算术右移 | >> | 高位补符号位(有符号数) | 1010 >> 2 = 1110 (假设4位补码) |
3. 运算优先级
优先级从高到低:~
→ <<
, >>
→ &
→ ^
→ |
注意:建议使用括号明确优先级,避免混淆。
二、算法竞赛高频套路
1. 位掩码(Bitmask)
- 用途:用整数二进制位表示集合状态(如子集、开关状态)。
- 示例:枚举集合
{a, b, c}
的所有子集:
// 枚举所有子集
vector<int> nums = {1, 2, 3};
int n = nums.size();
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]);
}
}
// 处理 subset
}
2. 快速判断与操作
- 奇偶判断:
x & 1
→1
为奇数,0
为偶数。 - 最低位的1:
lowbit = x & -x
(如12 (1100) → 4 (100)
)。 - 判断2的幂:
(x & (x-1)) == 0
(如8 (1000) → True
)。
3. 异或(XOR)魔法
- 交换变量:
a ^= b; b ^= a; a ^= b;
(无需临时变量)。 - 找唯一数:数组中其他数出现两次,找唯一出现一次的数:
a^a=0 ,b^0=b
int singleNumber(vector<int>& nums) {
int res = 0;
for (int num : nums) res ^= num;
return res;
}
- 消除重复:
x ^ x = 0
,x ^ 0 = x
。
4. 状态压缩动态规划
- 场景:用二进制位表示状态(如旅行商问题TSP、棋盘覆盖)。
- 示例:TSP问题中记录已访问城市的状态:
dp[mask][i] = 最短路径,其中 mask 表示已访问的城市集合,i 表示当前位置。
5. 位运算优化
- 快速乘除2的n次幂:对正数
x << n
等价于x * 2^n
,x >> n
等价于x // 2^n
。 - 快速幂算法:利用二进制分解指数:
ll mod_pow(ll a, ll b, ll mod) {
ll res = 1;
while (b) {
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
6. 统计二进制中1的个数
1.统计二进制中 1 的个数
int count = __builtin_popcount(x); // 32位整数
int count = __builtin_popcountll(x); // 64位整数
2. 前导零(Leading Zeros)
int leading_zeros = __builtin_clz(x); // 例如 x=1 (0001) → 31(32位下)
int leading_zeros = __builtin_clzll(x); // 64位版本
3. 末尾零(Trailing Zeros)
int trailing_zeros = __builtin_ctz(x); // 例如 x=8 (1000) → 3
7. 格雷码生成
- 二进制转格雷码:
gray = x ^ (x >> 1)
。
G i = B i + 1 ⊕ B i G_i = B_{i+1} \oplus B_i Gi=Bi+1⊕Bi
8. 函数
__lg() 函数是一个非标准的函数,只在部分编译器中支持,它返回一个整数值,表示输入参数的二进制表示中最高位1的位置(从0开始计数)。例如,__lg(8)将返回3,因为8的二进制表示是1000,最高位1在第3位。
log2() 函数是C++11标准中提供的函数,它返回一个浮点数值,表示以2为底的对数。例如,log2(8)将返回3.0,因为8等于2的3次方。它们的返回值类型不同,前者返回整数,后者返回浮点数。另外,__lg()是非标准函数,只在部分编译器中支持,而log2()是C++11标准库中的函数,在大部分编译器中都可以使用。
__builtin_clz() 前导零统计函数
__builtin_clz(unsigned int x) //用于无符号整数
int__builtin_clzl(unsigned long x) //用于无符号长整数
int__builtin_clzll(unsigned long long x) //用于无符号长长整数
//求最高有效位的位置
unsigned msb(unsigned x)
{
return sizeof(unsigned) * CHAR_BIT - 1 - __builtin_clz(x);
}
//功能上等价于
__lg(x)
floor(log2(x))
__builtin_popcount() 统计二进制中为1的位数的函数
__builtin_popcount (unsigned int) //用于无符号整数
三、注意事项与陷阱
- 位移溢出:左移超过数据类型位数时结果未定义(如
1 << 100
在32位整数中会溢出)。 - 符号位问题:右移有符号数时使用算术右移(负数补1),可能导致意外结果。
- 优先级混淆:
a & b == c
实际等价于a & (b == c)
,需用括号明确优先级。