位运算是一种基于整数二进制表示的运算,使用位运算在一些情况下可以有效提高算法效率。这篇博客主要对位运算进行总结,收集一些常用的位运算操作,并介绍几道例题。

基本的位运算分为六种,分别是按位与,按位或,按位异或,按位取反,左移,右移。

与,或,异或

这三个运算符都是涉及两个数的运算符。它们的作用可以通过下面这张表格给出

  运算符 符号表示 解释
& and 当两数对应的位都为1时运算结果为1,否则为0
| or 当两数对应的位都为0时运算结果为0,否则为1
异或 ^ xor 当两数对应的位相等时运算结果为0,否则为1

举个例子

6=(110)2,7 = (111)2

6&7 = (110)= 6

6|7 = (111)= 7

6^7 = (001)= 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 }

例题集合