学习内容
希望各位伙伴在刷力扣的二进制专题时,当然也是我自己的查找字典。后期在做题过程中会不定时补充,不需要左右查找,希望这个可以成为一个二进制字典~
二进制 | 常用知识点梳理
基础位运算
| : 按位或操作,两者有一个为1结果就是1
性质
置1性:1 | 任意二进制位 都为1 (可将目标位强制设定为1 )
保位性: 0 | 任何二进制位 = 原位 (不会清除原有的1,仅对0位生效)
非递减性: 执行 x | y 后,结果数值 >=x 且 >= y (只会增1或保位,不会减位)
&简单用法
1、将数字的第k位强置为1(从最低位0开始,位索引从0开始计数),1 << k 生成 指定位为1,其余位为0的掩码,与 x 进行按位或后,第k位会强制置为1,其他位保持不变
1 << 0 = 1 [01]
1 << 1 = 2 [10]
将4的第0位设定位1
4 [0100] | (1 << 0 ) = 5 [0101]
使用场景:位状态修改、二进制补位、状态压缩标记
2、提取数字的有效二进制位(补全低位0位1,保留高位1),
通过连续或运算,将数字二进制中最高位1以下的所有0位 置1,快速得到全1的地位掩码
x = 4 [100] ,
x |= x >> 1; // x = 4 | 2 = 6 [110]
x |= x >> 2; // x = 6 | 1 = 7 [111]
使用场景:生成自定义掩码、二进制位补全、全1数构造
3、合并两个数组的二进制1的位置,两个数的二进制位中,任意一个数的某一位位1,那么结果对应该位即为1
3 [011] | 5 [ 101] = 7 [111]
4 [100] | 6 [110] = 6 [110]
使用场景: 多状态合并、二进制特征整合、位信息汇总
4、快速将负数转为全位1掩码,计算机中的负数按照补码存储,负数最高位均位1 , 通过 x | 0 可快速保留补码特性,或通过 ~0|x 直接得到全1数
-1补码为32位全1 (补码:反码+1)
~0相当于-1( ~0对全0取反,结果为全1,对应十进制-1)
~0 | 5 = ~0 -1 | 4 = -1 (意义是验证或运算和全1特性
使用场景: 掩码生成、负数位运算辅助处理
cpp
//统计-5的二进制中1的个数
int n = (-5);
int mask = -1; //或~0
unsigned int un = n & mask; //负数转为无符号数
int count = 0;
while(un) { count ++; un &= un -1; //清除最后一个1}
return count;
&高阶用法
1、二进制状态压缩
用整数的每一位表示一个独立状态(如元素是否选中、字符是否出现、节点是否访问),通过|将目标位设置为1 , 完成状态标记。 int的32位可记录32个状态,long long的64位可记录64个
cpp
mask |= (1 << k) (将mask的第k位置1)
int mask = 0;
for(auto c : str) mask |= 1 << ( c - 'a' );
使用场景: 子集枚举、字符去重、状态压缩DP、路径标记
2、颠倒二进制位/位重排
在颠倒二进制位时,先通过左移为结果留位,在通过|将原数的指定位赋值到结果的目标位,逐位构建最终结果
res = ( res << 1 ) | ( n & 1 ) (每次取n的最低位赋值给res的最低为,res左移留位)
使用场景:自定义位重排、颠倒二进制位
3、配合掩码实现精准位替换
先构造掩码(目标位0、待替换位1),将原数与掩码或运算,把待替换位置1,再结合与运算完成精准替换
x = x | 掩码 ; x = x & ~替换掩码
8 **[1000]**低3位替换成5 [0101] 掩码使用7 [0111] , 8 | 7 = 15 [1111] ,再 15 & ~2 = 13 [1101]
使用场景:二进制位定制化修改、位重组、掩码组合操作
4、无符号数的高位补0辅助处理
对于有符号数,右值会补符号位,先通过 | 0保证位的原始状态,再结合或运算保留移位后的有效位,避免符号位导致的错误
n = n >>> 1 | 0 (无符号右移后保位)
使用场景:负数二进制位遍历、无符号位运算模拟
5、快速构造连续全1的二进制掩码
构造低m位全1掩码
cpp
int mask = (1<< m) -1; //基础方法
int mask = 0;
if ( m > 0) mask = (mask | (1 << (m-1))) -1 | (1 << ( m -1) ); //兼容m=0
使用场景:自定义位操作、精准位提取清除、动态掩码生成
| 避坑操作
1、优先级问题
| 的优先级低于算术运算、逻辑运算,如果混合运算必须加括号,且算术运算 > 移位运算 > & > ^ > | > 逻辑运算 > 赋值原酸
2、不可逆性:| 操作不可逆,无法反推原始x 与 y
3、数值放大风险: 因 | 操作会新增1的位,运算后数值可能大幅度增大,需要处理溢出用long long / long 来接收结果,或强转无符号,避免溢出
4、负数处理
负数以补码形式存储,高位全为1,与负数执行或运算后,结果必为负数
5、状态压缩位限制
用 mask |= (1 << k)标记状态时,k不能超过变量的位数( int 最大k = 30 ,long long 最大为62 , 避免移位溢出)
6、冗余置1无影响:对于已为1的位重复执行置1操作,结果不变,无需额外判断是否为1,可执行重复赋值操作