Bit Manipulation
二进制计算基本概念
操作符 | 功能 | 解释 |
---|---|---|
~ | 位求反 | 将操作数的每一个二进制位取反 |
<< | 左移 | 将左操作数的各个位向左移动右操作数位 |
>> | 右移 | 将左操作数的各个位向右移动右操作数位 |
& | 位或 | 两个操作数均为1结果为1,否则为0 |
^ | 位异或 | 对于每个位,两个操作数对应的位只有一个为1,则结果该位为1,否则为0 |
¦ | 位或 | 对于每个位,两个操作数至少一个为1,则结果该位为1,否则为0 |
注
强烈建议用unsigned整型操作数。
<<和>>的右操作数必须不是负数,并且必须严格小于左操作数位数的值,否则操作结果未定义。这两个操作都是用0补充空位,并丢弃移出去的位。
一个手动计算的示例
二进制乘法:
1 | 0011 * 0101 |
其中第三列的可以用一些技巧计算:
- 0110 + 0110等价于0110 * 2,也等价于把0110左移一位。
- 因为0100等价于4,也就是。凡是乘以的操作都可以通过左移n位来计算。所以将0011左移2位得到1100。
- 每一位和自己的取非值进行XOR操作都是1。所以
a^(~a)
的结果永远是一列1。 - 一个像
x&(~0<<n)
的操作会将x最右边的n位清0。通过进行~0<<n
操作,我们得到一个1右面跟着n个0。再与x进行AND操作,会将x的最右n位清0。注意~0
和1
可不一样哦。~0
是11111111,1
是00000001。
1 |
|
位操作相关的运算符优先级
Precedence | Operator | Description | Associativity |
---|---|---|---|
3 | ~ | Bitwise NOT | Right-to-left |
7 | << >> | Bitwise left shift and right shift | Left-to-right |
10 | & | Bitwise AND | Left-to-right |
11 | ^ | Bitwise XOR(exclusive or) | Left-to-right |
12 | ¦ | Bitwise OR(inclusive or) | Left-to-right |
15 | <<= >>= | Assignment by bitwise left shift and right shift | Right-to-left |
15 | &= ^= ¦= | Assignment by bitwise AND, XOR, and OR | Right-to-left |
常见计算技巧
常见二进制操作
Get Bit(得到某一位的Bit值)
方法是把1左移i位,得到一个类似00010000的值。再与num进行一个AND操作,就可以把第i位之外的值都清0。最后,把得到的值与0进行比较,如果结果不是0,那么第i位一定是1,否则一定是0。
1 | bool get_bit(int num, int i) |
Set Bit(设置某一位的Bit值为1)
方法是把1左移i位,得到一个类似00010000的值。再与num进行一个OR操作,只有第i位的值会被影响。
1 | int set_bit(int num, int i) |
Clear Bit(清零某位的Bit值)
方法差不多是SetBit的反转。首先得到一个类似00010000的值,然后取反得到11101111。然后与num进行AND操作。这样可以让某位变成0,同时保持其他位不受影响。
1 | int clear_bit(int num, int i) |
如果想把包含i在内一直到到最左边的所有位都清零。
1 | int clear_i_to_leftmost_bit(int num, int i) |
如果想把最右边到包含i在内的所有位都清零。
1 | int clear_rightmost_to_i_bit(int num, int i) |
Update Bit(设置某位的Bit值)
方法差不多是把SetBit和ClearBit组合起来。首先,通过用一个长得像11101111的遮罩把num第i位的值清0。然后把想设置的值v左移i位,这样会得到一个除了第i位是v,其他位都是0的数。然后让这两个数进行OR操作,第i位就会变成v的值。
1 | int update_bit(int num, int i, int v) |
STL中的bitset类型
需要包含头文件:
1 |
|
bitset对象的定义和初始化
bitset类型对象的区别仅在其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,须在尖括号里给出它的长度值:
1 | bitset<32> bitvec; // 32 bits, all zero |
给出的长度值必须是常量表达式。必须定义为整型字面值常量或是已用常量值初始化的整型的const对象。
初始化bitset对象的方法
代码 | 说明 |
---|---|
bitset<n> b; |
b有n位,每位都为0 |
bitset<n> b(u); |
b是unsigned long型u的一个副本 |
bitset<n> b(s); |
b是string对象s中含有的位串的副本 |
bitset<n> b(s, pos, n); |
b是s中从位置pos开始的n个位的副本 |
用unsigned值初始化bitset对象
十六进制值0xffff表示为二进制位就是十六个1和十六个0(每个0xf可表示为1111)。
1 | // bitvec1 is smaller than the initializer |
用string对象初始化bitset对象
从string对象读入位集的顺序是从右向左。即string对象最右边字符(下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。
1 | string strval("1100"); |
bitset对象上的操作
bitset操作
代码 | 说明 |
---|---|
b.any() | b中是否存在置为1的二进制位? |
b.none() | b中不存在置为1的二进制位吗? |
b.count() | b中置为1的二进制位的个数,返回值类型是size_t |
b.size() | b中二进制位的个数,返回值类型是size_t |
b[pos] | 访问b中在pos处的二进制位 |
b.test(pos) | b中在pos处的二进制位是否是1? |
b.set() | 把b中所有二进制位都置为1 |
b.set(pos) | 把b中在pos处的二进制位置为1 |
b.reset() | 把b中所有二进制位都置为0 |
b.reset(pos) | 把b中在pos处的二进制置为0 |
b.flip() | 把b中所有二进制位逐位取反 |
b.flip(pos) | 把b中在pos处的二进制位取反 |
b.to_ulong() | 用b中同样的二进制位返回一个unsigned long值 |
os << b | 把b中的位集输出到os流 |
测试整个bitset对象
1 | bitset<32> bitvec; // 32 bits, all zero |
访问bitset对象中的位
1 | // assign 1 to even numbered bits |
对整个bitset对象进行设置
1 | bitvec.reset(); // set all the bits to 0 |
获取bitset对象的值
仅当bitset类型的长度小于或等于unsigned long的长度时,才可以使用to_ulong操作:
1 | usigned long ulong = bitvec3.to_ulong(); |
输出二进制数
1 | bitset<32> bitvec2(0xffff); // bits 0...15 are set to 1; 16...31 are 0 |
输出结果为:00000000000000001111111111111111
使用位操作符
bitset类也支持内置的位操作符。
Using Bitsets as Sets of Flags
1 |
|
一些利用二进制思路解决的题目
[Brute Force] | Subsets
[Brute Force] | Subsets II
[Array] | Gray Code
[Array] | Single Number
[Array] | Single Number II
[Brute Force] | Power of Four
[1] C++ Primer 5th Edition
[2] Cracking the Code Inteview
[3] The C++ Standard Library 2nd Edition
[4] http://en.cppreference.com/w/cpp/language/operator_precedence
[5] leetcode