算法入门 --- 位运算
α. 位运算的定义
数据在计算机里以二进制的形式存在,所谓位运算便是直接对数据在内存中的二进制位进行操作.
β. 位运算的形式(参与运算的数以补码进行运算)
β1. &(位与)
1 & 1 = 1
0 & 1 = 0
1 & 0 = 0
0 & 0 = 0
我们常常利用 & 运算把 0 消掉,而保持其他位的1不变,除此之外,我们还可以判断一些数的性质,下面会提到.
a & b = 1 (当且仅当a,b均为1)
β2. |(位或)
1 | 1 = 1
0 | 1 = 1
1 | 0 = 1
0 | 0 = 0
a | b = 0 (当且仅当a,b均为0)
β3. ^(位异或)
1 ^ 1 = 1
0 ^ 1 = 0
1 ^ 0 = 0
0 ^ 0 = 1
a ^ b = 1(当且仅当a=b)
β4. << >> (位左移,位右移)
左移(a<<b):将a向左移b位,后面补0,左移的结果为a增大2^b倍
| a | b | a<<b |
|---|---|---|
| 0000000 00000000 00000000 01011000 | 3 | 0000000 00000000 00000010 11000000 |
| 88 | 增大8倍 | 704 |
右移(a>>b)
如果a为正整数, 将a带符号向右移b位,高位补零,右移的结果为a的2^b倍.
如果a为负整数,右移的结果为a/2的b次方向上取整,具体解释可以参考这篇文章—>传送门
| a | b | a>>b |
|---|---|---|
| 0000000 00000000 00000000 01011000 | 3 | 0000000 00000000 00000000 01011000 |
| 88 | 减小8倍 | 11 |
| 10000000 00000000 00000000 01100100 | 4 | 10000000 00000000 00000000 00000111 |
| -100 | 减小16倍 | -7 |
对于正整数来说,左移右移的操作和乘或者除2的幂次的效果是一样的,我们经常可以用位运算代替乘除法,因为位运算更底层,乘法和除法的本质也就是多次位运算,相比较而言,直接使用位运算速度更快.
β5. ~ (取反)
~0 = 1
~1 = 0
γ. 位运算的巧妙应用
γ1. 判断奇偶性(n&1)
一般我们判断数据的奇偶性时,会通过整数能否被2整除来判断,但从二进制形式来看,对于一个奇数,它的二进制形式最后一位一定是一(最右边一位权值为1,其他权值均为2的正整数倍),如果最右边一位为一,它肯定不能被2整除,即为奇数,反之为偶数.
判断一个数二进制形式最右边一位是否为1可以用到 & ,对于一个数 n,n & 1的结果便是把其最后一位取出来,我们再进行判断0 | 1 即可
11001
& 00001
——————
00001
所以在编程时,我们可以使用位运算来代替取余判断
if (n % 2 == 0) { // 如果为偶数
}
if (n & 1) { // 如果为奇数
}
γ2. 快速乘,快速幂
这里我请参考我的另一篇文章—>传送门,大致思想就是通过位运算,将计算a*b 以及a^b的计算时间复杂度转换为O(log(n)).
γ3. 判断一个数是否为2的指数幂(n&(n-1))
正常情况下我们判断一个数是否为2的指数幂的思路为:一直被2整除直到结果变成1或者非1奇数,这种思路的时间复杂度为O(log(n)),而使用位运算可以达到O(1)的效果.
一个二次幂的正整数n,它的二进制形式(不考虑存储的位数),为最左边一个1,接着右边全为0,而对于n-1,它相较于n最左边变为0,右边全为1,不难发现,二者相与结果为零,并且可以证明非二次幂的正整数n&(n-1)不会为0(这里就不花费篇章证明了).
| n | n-1 | n&(n-1) |
|---|---|---|
| 1000000 | 0111111 | 0000000 |
| 100011 | 100010 | 100011 |
γ4. 找出唯一一个只出现一次的数(其他都是成对出现的)
a ^b ^b = a
如果给定我们一系列乱序的书页码(共n页),让我们从中找到,丢失的一页,这个性质便派上了用场,我们先枚举出1-n并相异或,再和输入的书页码相异或,这样,除了丢失的页码只出现一次,其他页码均出现两次,便得到了我们寻找的答案.
γ5. 枚举子集
这里笔者会用另外一篇文章来讲解,大家尽请期待~

浙公网安备 33010602011771号