目录
一.写在前面
以前我也没怎么重视过位运算,直到我开始刷leetcode......才发现,合理运用位运算可以优化算法,提高效率。
我们都知道,现代计算机中的数据都是以二进制形式存储的,我们人类习惯处理的是十进制的数据,但是计算机处理的是二进制的数据,在我们使用计算机处理数据的时候,计算机往往要先把我们输入的十进制数据转为二进制数据再进行处理,如果我们能直接对二进制数据进行处理,那我们就能在原本程序的基础上,进一步提高运行效率。
计算机对二进制数据进行的运算,就叫位运算。
二.位运算符
1.二进制
几进制就是指一个位上最多能表示几个数,例如我们习惯的十进制,一位上有0-9十个数字。
而我们今天要讲的二进制,一位上自然就是0、1两位数字。
在我们编程的过程中,我们的最小单位是字节(Byte),我们知道,一个字节有8位,这里的8位指的就是8个位(bit),一个位代表一个0或者一个1。
2.位运算符
在讲位运算符之前,我们应该知道:Java定义的位运算直接对整数类型的位进行操作,包括了long,int,short,char和byte,也就是说我们不能对double,float和boolean进行位运算。
Java中的位运算符分为 位逻辑运算符(& | ~ ^) 和 位移运算符(<< >> >>>)两类。
(1)&(与)
按位与运算符(&),其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位同时为 1,那么计算结果才为 1,否则为 0。因此,任何数与 0 进行按位与运算,其结果都为 0。
1&1=1
0&0=0
1&0=0
0&1=0
即两位同时为1,结果才为1,否则结果为0。
举个例子:
00000000111001010
00000000010101101
00000000010001000
注意:按位与运算符和逻辑与运算符虽然写法一致,但是按位与运算是对二进制位上的数值进行计算,逻辑与运算符是对布尔型数据进行运算。
常见用途:
- 清零:如果想将一个单元清零,只要与一个各位都为零的数值相与,结果为零。
- 取一个数的指定位:若想取某数的指定几位,只需找另一数,令对应位数为1,其余位为0,然后将两数进行按位与运算,即可得到指定数的指定几位
- 判断奇偶:只要根据最末位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((num & 1) == 0)代替if (num % 2 == 0)来判断num是不是偶数。
(2)|(或)
按位或运算符(|),其运算规则是:参与运算的数字,低位对齐,高位不足的补零。如果对应的二进制位只要有一个为 1,那么结果就为 1;如果对应的二进制位都为 0,结果才为 0。
0|0=0
0|1=1
1|0=1
1|1=1
即参加运算的两个对象只要有一个为1,其值为1。
举个例子:
00000000101010001
00000000010000010
00000000111010011
常见用途:
- 常用来对一个数据的某些位设置为1:若要将一个数的指定几位设为1,只需要再找一个数,使得他指定的几位为1,其余位为0,然后将两数进行按位或运算即可得到。
(3)~(取反)
按位取反运算符为(~),其运算规则是:只对一个操作数进行运算,将操作数二进制中的 1 改为 0,0 改为 1。
注:" ~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
~1=0
~0=1
即对一个二进制数按位取反,即将0变1,1变0。
举个例子:
~1010111=0101000
常见用途:
- 使一个数的最低位为零:使a的最低位为0,可以表示为:a & ~1。~1的值为 1111111111111110,再按"与"运算,最低位一定为0。
注:7种位运算符中,仅有按位取反运算符(~)是单目运算符,其他运算符是双目运算符。
(4)^(异或)
按位异或运算符为(^),其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位相同(同时为 0 或同时为 1)时,结果为 0;如果对应的二进制位不相同,结果则为 1。
0^0=0
1^1=0
0^1=1
1^0=1
即参加运算的两个对象,如果两个相应位相同为0,相异为1。
举个例子:
0000001010111
0000001010001
0000000000110
按位异或运算符的性质:
- 交换律
- 结合律: (a^b)^c == a^(b^c)
- 对于任何数x,都有 x^x=0,x^0=x
- 自反性: a^b^b=a^0=a
常见用途:
- 翻转指定位:若要将某数的指定位翻转,只需要找另一个数,令它的指定位为1,其余位为0,两者再进行异或运算即可得到。
(5)<<(左移)
左移位运算符(<<),其运算规则是:按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
举个例子:
移位前:011010
移位后:110100
移位后的数相当于移位前的数的2倍。
(6)>>(带符号右移)
带符号右移位运算符(>>),其运算规则是:按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补符号位上的数字。
举个例子:
移位前:10010100
移位后:11001010
移位后的数相当于移位前的数除以2。
(7)>>>(无符号右移)
无符号右移位运算符(>>>),其运算规则是:按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位补零。
举个例子:
移位前:10000101
移位后:01000010
移位后的数相当于移位前的数除以2。
(如有错误,欢迎指正。)