在计算机中所有数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快。
一、二进制的表现形式
计算机内部存储、计算的任何信息都是由二进制(0和1)表示,而二进制有三种不同的表示形式:原码、反码和补码。计算机内部使用补码来表示。
原码:其二进制表示(有一位符号位)
反码:正数的反码就是原码,负数的反码是符号位不变,其余位取反
补码:正数的补码就是原码,负数的补码是反码+1
符号位,最高位为符号位,0表示正数,1表示负数。在位运算中符号位也参与运算。
二、基本位操作
基本的位操作符有与、或、异或、取反、左移、右移这6种,如下所示:
符号 | 描述 |
---|---|
& |
按位与 |
| |
按位或 |
^ |
按位异或 |
~ |
按位非 |
<< |
左移 |
>> |
右移 |
按位与&
运算规则:其中两位都为1结果为1,其他情况为0
运算符:二目运算符
示例:
3 & 17 = 1
3 = 00000011
17 = 00010001
3 & 17 = 00000001
按位或|
运算规则:两个位都为0时,结果才为0,至少有1位为1结果就为1
运算符:二目运算符
示例:
3 | 17 = 19
3 = 00000011
17 = 00010001
3 | 17 = 00010011
按位异或^
运算规则:两个位相同为0,相异为1
运算符:二目运算符
示例:
3 ^ 17 = 18
3 = 00000011
17 = 00010001
3 ^ 17 = 00010010
小技巧
异或是不进位加法,两个数做加法,把进位舍去
按位非~
运算规则:对操作数进行按位取反,原来为1结果为0,原来为0结果为1。
运算符:单目运算符
示例:
~9 = -10
转二进制:0 1001
计算补码:0 1001
按位取反:1 0110
转为原码:
按位取反:1 1001
末位加一:1 1010
符号位为1是负数,即 ~9 = -10
小技巧
所有正整数的按位取反是其本身+1的负数
所有负整数的按位取反是其本身+1的绝对值
零的按位取反是 -1
左移<<
运算规则:把操作数整体向左移动
运算符:二目运算符
示例:
33 << 2 = 00100001 << 2 = 10000100 = 132
小技巧
$a << n = a * 2^n$ (a为正数)
右移>>
运算规则:把操作数整体向右移动
运算符:二目运算符
示例:
33 >> 2 = 00100001 >> 2 = 00001000 = 8
小技巧
$a >> n = a / 2^n$ (a为正数)
位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。
三、常用的位操作技巧
1、判断奇偶数
只需要判断已知数的二进制数末尾是0还是1,如果为0就是偶数,为1就是奇数。
求0-50之间所有的偶数代码示例如下:
for (int i = 0; i < 50; i++) {
if ((i & 1) == 0) {
System.out.println("0-50之间所有的偶数:" + i);
}
}
2、不用加减乘除做加法
异或是按位相同为0不同为1,其实就是做加法的过程把进位舍去了。如果两数相加没有进位就直接可以使用异或,如果有进位,就把进位加上。
有进位的地方就是两个位都为1的地方,这就可以利用按位与运算了,把两数相与的结果左移一位就是进位的结果。
a + b = (a ^ b) + ((a & b) << 1)
不用加减乘除做加法的代码示例如下:
// 循环法1
class Solution {
public int add(int a, int b) {
while(b != 0) { // 当进位为 0 时跳出
int c = (a & b) << 1; // c = 进位
a ^= b; // a = 非进位和
b = c; // b = 进位
}
return a;
}
}
// 递归法2
public class Solution {
public int Add(int num1,int num2) {
if (num2 == 0) return num1;
return Add(num1 ^ num2, (num1 & num2) << 1);
}
}
3、只出现一次的数字
两个相同的数异或为0,一个数和0异或结果不变,那么异或两次相当于没有异或,即a ^ b ^ b = a
给一个数组,数组中的数字只有一个出现了一次,其他的都出现了两次,找出这个只出现一次的数字,代码示例如下:
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for (int i : nums) {
res ^= i;
}
return res;
}
}