位运算是一种基于整数二进制表示的运算,使用位运算在一些情况下可以有效提高算法效率。这篇博客主要对位运算进行总结,收集一些常用的位运算操作,并介绍几道例题。
基本的位运算分为六种,分别是按位与,按位或,按位异或,按位取反,左移,右移。
与,或,异或
这三个运算符都是涉及两个数的运算符。它们的作用可以通过下面这张表格给出
运算符 | 符号表示 | 解释 | |
与 | & | and | 当两数对应的位都为1时运算结果为1,否则为0 |
或 | | | or | 当两数对应的位都为0时运算结果为0,否则为1 |
异或 | ^ | xor | 当两数对应的位相等时运算结果为0,否则为1 |
举个例子
6=(110)2,7 = (111)2
6&7 = (110)2 = 6
6|7 = (111)2 = 7
6^7 = (001)2 = 1
取反
按位取反是对一个数进行的运算,它是一个单目运算符,它的含义是对一个数二进制表示中每一位都取反(0变成1,1变成0)。其运算符为~。有符号整数的符号位在取反运算中同样会取反。
补码
取反运算可以用来求补码。正数和零的补码是它自身,负数的补码是它对应正数按位取反后加一。
6 = (00000110)2
6的补码=(00000110)2
-6的补码 = (11111010)2
左移和右移
顾名思义,左右位移是对一个二进制数对向右或向左移动的操作。左移记为n<<i,n为要被移动的二进制数,i为要被移动的位数,右移同理。还是举个例子
8 = (00001000)2
8<<2 = (00100000)2
8>>2 = (00000010)2
无效行为
1. 移动位数为负数。
2. 超出被位移数的数据范围。
补位
左移运算,高位丢弃,低位补 0。
右移运算,对无符号数,高位补0,对有符号数,高位补符号位。
位运算的常见操作
通过六种位运算符配合,可以完成一些意想不到的操作,下面总结一些常见的
1 .使用异或完成两数的交换
异或运算有一个特点,就是一个数连续异或另一个数的结果不变,即a^b^b=a,利用这个性质可以实现两个整数的交换。
1 void xorSwap(int&a,int&b) 2 { 3 a = a^b; 4 b = a^b; //相当于a连续两次异或b 5 a = a^b; //相当于b连续两次异或a 6 }
但要注意,这种异或方式只能用在整数上,而且效率也比较低,所以尽量不要使用。
2. 取二进制的从第向高数的第K位(最低位编号为0)
1 int getKbit(int a,int K) 2 { 3 return (a>>K)&1; 4 }
3.取二进制数从低向高数的最后k位(最低位编号为0)
1 int getKbits(int a,int K) 2 { 3 return a&((1<<K)-1); 4 }
4. 将二进制数的某一位置为0
1 int unsetKbit(int a,int K) 2 { 3 return a&(~(1<<K)); 4 }
5. 将二进制数的某一位置为1
1 int setKbit(int a,int K) 2 { 3 return a|(1<<K); 4 }
6. 将二进制数的某一位取反
1 int flapKbit(int a,int K) 2 { 3 return a^(1<<K); 4 }
7. 得到二进制数的最后一个1
1 int getLastOnes(int a) 2 { 3 return a&(-a); 4 5 }
8. 清除末尾连续的1
1 int clearLastOnes(int a) 2 { 3 return a&(a+1); 4 }
9. 判断两非零数符号是否相等
1 bool isSameSign(int a,int b) 2 { 3 return (x^y)<0; 4 }
10. 判断一个数是不是2的幂次方
1 bool isPowerOfTwo(int a) 2 { 3 return a&(a-1)==0&&n>0; 4 }
11. 交换符号
1 int reversal(int a) 2 { 3 return ~a+1; 4 }
例题集合