一、位运算
位运算有6种,只能用于整型操作数。使用位运算不会改变原来的数。
优先级别由高到低依次为:按位取反( ~ ),移位(<<或>>),按位与( & ),按位异或( ^ ),按位或( | )。
注:只考虑正整数。
1.按位取反( ~ )
~ 是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变成0。
~ 0 = 1
~ 1 = 0
2.按位与(&)
&是二元操作符。当相应位上的数都是1时,该位取1,否则该为0。(有0取0,没0取1)
0 & 0 = 0 1 & 1 = 1
0 & 1 = 0 1 & 0 = 0
3.按位异或( ∧ )
∧ 是二元操作符。当相应位置上的数相同,该位取0,如果不同该位取1。(不同为1,相同为0)
0 ^ 0 = 0 1 ^ 1 = 0
0 ^ 1 = 1 1 ^ 0 = 1
4.按位或(|)
| 是二元操作符。只要相应位上存在1,那么该位就取1,如果都不为1,就为0。(有1取1,没1取0)
0 | 0 = 0 1 | 1 = 1
0 | 1 = 1 1 | 0 = 1
5.左移 ( << )
将一个数二进制下的数向左移若干位,空出的位用0填充,并且丢弃移出左侧操作数末端的位。
n << k = n * (2 ^ k)
6.右移( >> )
将一个数在二进制下右移若干位,丢弃移出左侧操作数右端的位。使用0填充左端空出的位。
n >> k == n / (2 ^ k)
二、位运算技巧
1.基础技巧
x & 1 判断奇偶
(x >> k) & 1 判断二进制 x 的第k位是 1 还是 0
x & (1 << k) 判断二进制 x 的第k位是 1 还是 0
-x = ~x+1 负数是原码取反+1
2.lowbit( x ) ,用于计算 x 的二进制表示中最低位 1 的大小
int lowbit(int x){
return x & (- x);
}
3.消除x的最后一位一
x = x & (x-1)
三、异或
1.运算法则
交换律: a ^ b = b ^ a
结合律: a ^ b ^ c = a ^ (b ^ c)
自反: b = a ^ b ^ a
若 d = a ^ b ^ c 可以推出 a = d ^ b ^ c
按位异或特点:
x ^ 0 = x 0异或任何数等于任何数
x ^ x = 0 任何数异或自己等于 0(可以判断两个值是否相等)
x ^ (1 << k) 使某些特定的位翻转
n ^ 1 = n + 1 当 n 为偶数时
n ^ 1 = n - 1 当 n 为奇数时
四、STL bitset
1.bitset 概念
bitset 可看作一个多位二进制数,每8位占用1个字节,相当于采用了状态压缩的二进制数组,并支持基本的位运算。
在估算程序运行的时间时,一般以32位整数的运算次数为基准,因此 n 位bitset执行一次位运算的复杂度可视为n/32,效率较高。
注:头文件 #include< bitset >
2.位运算操作符
bitset<n> s 表示一个n位二进制数
~ s 返回对s按位取反的结果。
&,|,^ 返回对两个位数相同的bitset执行按位与、或、异或运算的结果
>>,<< 返回把个bitset右移、左移若干位的结果。
==, != 比较两个bitset代表的二进制数是否相等。
3.基本操作
s.count() 返回有多少位为1。
any/none 若s所有位都为0,则s.any()返回false, s.none()返回true.
若s至少一位为1,则s.any()返回true , s.none()返回false.
s.set() 把s所有位变为1
s.set(i,k) 把s的第i位改为k,即s[i]=k
s.reset() 把s所有位变为0
s.reset(i,k) 把s的第i位改为0,即s[k]=0。
s.flip() 把s的所有位取反,即s=~s
s.flip(k) 把s的第k位取反,即s[k]^=1。