位运算的基本概念
在计算机中的数在内存中都是以二进制的形式存储的,位运算可以直接对内存中的二进制进行操作,相比在代码中直接使用(+、-、*、/)运算符,合理使用位运算能大大的提高程序的性能。
这里以整型为例,在计算机中,整型是以32为二进制存储的.
整数的最高位为符号位,正数为0,负数为1
10000000000000000000000000000000 <= int <=01111111111111111111111111111111
所以int的范围为:-231<= int <=231-1
负数化成十进制为:最高位不变,其余取反+1
正数直接二进制化成十进制就可以了,正负数转换见(~)取反运算
位操作符
与运算(&)
与运算两个为都是1 的结果为1,否则为0
注意:负数按补码的形式按位与运算
用与运算判断奇偶,只要根据末尾是0还是1,为0就是偶数,为1就是奇数。就可以用 (x & 1 == 0)来判断是不是偶数。
代码:
//打印偶数
public class code01 {
public static void main(String[] args) {
for (int i=0;i<=10;i++){
if((i & 1)==0){
System.out.print(i+" ");
}
}
}
}
或运算(|)
参与运算的两个对象只要有一个为1,其值就为1
1)常用来对一个数据的某些位设置为1
比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。
异或运算 (^)
相同为1,相宜为0
异或的几条性质:
- 交换律
- 结合律 ( a ^ b) ^ c == a ^ (b ^ c)
- 对于任何数a,都有a ^ a = 0 , a ^ 0 = a
- 自反性:a ^ b ^ b = a ^ 0 = a
使用异或解决问题
使用异或交换:
c = a ^ b;
b = c ^ b;
a = c ^ a;
只出现一次的数字:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
示例:
代码实现:
class Solution {
public int singleNumber(int[] nums){
int a=0;
for(int i=0;i<nums.length;i++)
a=a^nums[i];
return a;
}
}
取反运算符(~)
对一个二进制数按位取反,即将0变1,1变0。
取反加1,将正数变成负数,负数变成整数
public static int reversal(int a){
return ~ a + 1;
}
正数取反加1,正好变成其对应的负数(补码表示),负数取反加1,则变成其源码,即正数。
移位运算(<< >>)
左移(<<)
各二进制位全部左移若干位,高位丢弃,低位补零。
注意:左移符号不牵扯符号位的变换,最高位直接丢弃
通过左移符号打印整数的32位二进制:
代码展示:
public class Test {
public static void main(String[] args) {
//打印一个整数的32位二进制
print(5);
//整数最大值
int a=Integer.MAX_VALUE;
//最小值
int b=Integer.MIN_VALUE;
print(a);
print(b);
}
public static void print(int n){
for(int i=31;i>=0;i--){
System.out.print((n & (1<<i)) ==0 ? 0 : 1);
}
System.out.println();
}
}
右移(>>)
各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
注意:(>>)是带符号位移 ;(>>)是不带符号位移
常见位运算问题
移位操作进行高低位交换
给定一个16位的无符号整数,将其高8位与低8位进行交换,求出交换的值
a = (a >> 8) | (a << 8);
位操作实现乘除法
int a = 2;
a >> 1 =1
a << 1 =4;
位操作交换两数
位操作交换两数可以不需要第三个临时变量,虽然普通操作也可以做到,但是没有其效率高
public static void swap(int a,int b){//普通方法不需要零时变量
a = a+b;
b = a-b;
a = a-b;
}
public static void swap(int a,int b){//普通方法使用临时变量
int c = a;
a = b;
b = c;
}
public static void swap(int a,int b){//使用异或,不需要临时变量
a = a ^ b; //a ^= b ---> a = (a^b);
b = b ^ a; //b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a
a = a ^ b; //a ^= b ---> a = (a^b)^a = (a^a)^b = b
}
位操作求绝对值
整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作。
public static int abs(int a) {
int i = a >> 31;
return i == 0 ? a : (~a + 1);
}
更多位运算的操作可以深入了解,掌握好位运算大大提高代码的运行效率,是代码更简洁