跳转至

位运算

前置知识

二进制

目标

取反,按位与,按位或,按位异或,左移,右移,lowbit操作

取反

NOT,~

有符号整数的符号位在 ~ 运算中同样会取反

在二进制表示下,正数和 0 的补码为其本身,负数的补码是将其对应正数按位取反后加一

取反操作符用波浪线"~"表示。值得注意的是此操作符与“逻辑非(!)”操作符不同

C++中,~a

im g

有符号整数

对于无符号整数,数的按位补码,是其在无符号整数范围的中点另一边的“镜像”。例如,对于8位无符号整数,NOT x = 255 - x,可以在图上将其可视化为一条向下的线,相当于从 0 到 255 递增的范围,“翻转”到从 255 到 0 递减的范围。

按位与

AND,&

按位与处理两个长度相同的二进制数,两个相应的二进位都为1,该位的结果值才为1,否则为0。

此操作可以被用来检查一个特定的位是1还是0

C++中,a & b

img

按位或

OR,|

按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为 1,该位的结果值就为 1。

应用按位或操作可以将二进制数的某一位设为 1

C++中,a | b

img

按位异或

NOR,^

按位异或运算,对等长二进制模式或二进制数的每一位执行逻辑异或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0。

用值的自身对其执行按位异或运算将得到 0

C++中,a ^ b

img

移位

左移使用两个小于符号"<<"表示,右移使用两个大于符号">>"表示

用来将一个二进制数中的每一位全部都向一个方向移动指定位,溢出的部分将被舍弃,而空缺的部分填入一定的值。

算数左移:

img

算数右移:

img

左移一位乘以二右移一位除以二
n << m
n >> m

int x = -5;
cout << (x >> 1) << '\n';  // 向下取整,向负方向取整
cout << (x / 2) << '\n';   // 向0取整


判断两个非零数符号是否相同
bool isSameSign(int x, int y) {  // 有 0 的情况例外
  return (x ^ y) >= 0;
}

交换两个数
void swap(int &a, int &b) { a ^= b ^= a ^= b; }

获取二进制的某一位
int getBit(int a, int b) { return (a >> b) & 1; }

将二进制的某一位置成0
int unsetBit(int a, int b) { return a & ~(1 << b); }

将二进制的某一位置成1
int setBit(int a, int b) { return a | (1 << b); }

将二进制的某一位取反
int flapBit(int a, int b) { return a ^ (1 << b); }

计算绝对值
int Abs(int n) {
  return (n ^ (n >> 31)) - (n >> 31);
  /* n>>31 取得 n 的符号,若 n 为正数,n>>31 等于 0,若 n 为负数,n>>31 等于 -1
    若 n 为正数 n^0=n, 数不变,若 n 为负数有 n^(-1)
    需要计算 n 和 -1 的补码,然后进行异或运算,
    结果 n 变号并且为 n 的绝对值减 1,再减去 -1 就是绝对值 */
}

算数右移、逻辑右移

算数右移:

在二进制补码表示下,把数字同时向右移动,高位以符号位填充,低位越界后舍弃。

n >> 1,等于 n 除以 2 向下取整, (-3) >> 1 = -2, 3 >> 1 = 1

n / 2,在 C++ 中实现为“除以2向零取整”,(-3) / 2 = -1, 3 / 2 = 1

逻辑右移:

在二进制补码表示下,把数字同时向右移动,高位以 0 填充,低位越界后舍弃

C++语法没有规定右移的实现方式,我们默认右移操作采用算数右移方式实现

lowbit()

非负整数 n 在二进制下 “最低位的 1 及其后边所有的 0” 构成的数值

n = 10, 二进制为 \((1010)_2\) ,lowbit(n) = 2

int lowbit(int n) {
    return n & -n;  // n & (~n + 1)
}

// 运用lowbit操作,可以找出整数二进制表示下的所有是 1 的位
for (int i = 0; i <= 20; i++) H[1 << i] = i; // 预处理
while (n > 0) {
    cout << H[n & -n] << ' ';
    n -= n &  -n;
}
我们来看另外一个例子
x &= x - 1; // 干掉 x 的最右边的 1

总结

NOT,AND,OR,NOR,<<,>>

lowbit操作,n & -n,返回最低位的1及其后边所有的0构成的数值

参考

位运算 - OI Wiki

位操作

《算法进阶指南》