位运算的核心价值在于其直接操作二进制位的特性,相较于高级语言层面的算术或逻辑运算,能实现接近硬件底层的极致执行效率。该技术体系的基础由六种基本运算符构成,并通过一系列运算律与高级操作形成了其方法论基础。在处理负数时,需特别注意补码表示下的运算规则差异 。
一、核心运算符与运算律
位运算的基础是六种运算符,其运算规则如下表所示:
| 符号 | 描述 | 运算规则(二进制位) |
|---|---|---|
& |
按位与 | 两位均为1时,结果为1 |
| ` | ` | 按位或 |
^ |
按位异或 | 两位相同为0,相异为1 |
~ |
按位取反 | 0变1,1变0 |
<< |
左移 | 各二进位全部左移,高位丢弃,低位补0 |
>> |
右移 | 各二进位全部右移,对无符号数高位补0,有符号数处理方式因架构而异 |
这些运算符遵循特定的运算律,如交换律(A&B = B&A)、结合律((A&B)&C = A&(B&C))、等幂律(A&A = A)等,构成了位运算的逻辑基础 。
二、高级操作与经典应用
文章系统性地归纳了多种高级位操作技巧,这些是位运算灵活性的集中体现 。部分关键操作如下:
| 功能 | 示例(二进制) | 位运算表达式 |
|---|---|---|
| 取末k位 | ` | |
1011取末2位得 |
||
| 0011` | x & ((1<<k)-1) |
|
| 将右数第k位变为1 | ` | |
0001(k=4) 得 |
||
| 1001` | `x | (1<<(k-1))` |
| 将末k位全变为1 | ` | |
1000(k=3) 得 |
||
| 1111` | `x | ((1<<k)-1)` |
| 去掉最低位的1 | ` | |
1100得 |
||
| 1000` | x & (x-1) |
|
| 取出最低位的1 (lowbit) | ` | |
10100得 |
||
| 00100` | x & (-x) |
基于这些操作,衍生出多种高效应用:
-
快速计算 :利用左移(
<<)实现乘以2,右移(>>)实现除以2 。 -
状态判断 :通过与操作
(x & 1)判断奇偶性(结果为0是偶数,1是奇数)。 -
交换变量 :使用三次异或操作,无需临时变量即可交换两个整数 。
cppvoid swap(int &a, int &b) { a ^= b; b ^= a; a ^= b; } -
取模优化 :对形如
2^k的数p取模,等价于a & (p-1)。 -
统计1的个数 (Brian Kernighan算法) :通过反复执行
x = x & (x-1)消除最低位的1,直到x为0,循环次数即为1的个数 。
三、负数处理与特殊性质
负数的位运算基于其补码表示。一个关键性质是:~(-1) == 0,因为-1的补码为全1,按位取反后为全0。这一特性常被用于初始化标志(如链式前向星中的 head 数组初始化为-1)和循环终止条件判断 。lowbit 函数 x & (-x) 能快速获取二进制表示中最低位的1及其后的0,是树状数组等数据结构的核心操作 。
四、综合例题解析
文章通过具体例题展示了位运算的综合应用 。
-
更新二进制位 :将整数N的第i到j位替换为M。解决方案是先将N中i到j位清零,再与左移i位的M进行或操作。
cpp// 示例:将N的2到6位替换为M int updateBits(int n, int m, int i, int j) { for (int pos = i; pos <= j; pos++) { n &= ~(1 << pos); // 将第pos位清零 } return n | (m << i); // 合并M } -
A+B问题:尽管未在摘要中给出完整代码,但暗示了可通过位运算模拟全加器实现,利用异或运算得到无进位和,利用与运算及左移得到进位,迭代至进位为0 。