Java 常用的二进制操作
转载自:JAVA中常用的二进制位操作
补充:位运算符
在 Java 中,int 数据底层以补码形式存储。int 型变量使用 32bit 存储数据,其中最高位是符号位,0 表示正数,1 表示负数,可通过 Integer.toBinaryString() 转换为 bit 字符串,
// 若最高的几位为 0 则不输出这几位,从为 1 的那一位开始输出
System.out.println(Integer.toBinaryString(10));
System.out.println(Integer.toBinaryString(-10));
// 以上会输出(手工排版过,以下的输出均会被手工排版):
1010
11111111111111111111111111110110
左移 << 符号的使用
例如:5 << 2 = 20
首先会将 5 转为 2 进制表示形式: 0000 0000 0000 0000 0000 0000 0000 0101
然后左移 2 位后,低位补 0: 0000 0000 0000 0000 0000 0000 0001 0100
换算成 10 进制为 20
右移 >> 运算符
例如:5 >> 2 = 1
还是先将 5 转为 2 进制表示形式: 0000 0000 0000 0000 0000 0000 0000 0101
然后右移 2 位,高位补 0: 0000 0000 0000 0000 0000 0000 0000 0001
换算成十进制后是 1
无符号右移 >>> 运算符
例如:5 >>> 3
我们知道在 Java 中 int 类型占 32 位,可以表示一个正数,也可以表示一个负数。正数换算成二进制后的最高位为 0,负数的二进制最高为为 1。
对于 2 进制补码的加法运算,和平常的计算一样,而且符号位也参与运算,不过最后只保留 32位
-5 换算成二进制: 1111 1111 1111 1111 1111 1111 1111 1011
-5 右移3位: 1111 1111 1111 1111 1111 1111 1111 1111 // (用1进行补位,结果为-1)
-5 无符号右移3位: 0001 1111 1111 1111 1111 1111 1111 1111 // (用0进行补位,结果536870911 )
正数二进制中 1 的个数
//求解正数的二进制表示法中的 1 的位数
private static int countBit(int num){
int count = 0;
for(; num > 0; count++)
{
num &= (num - 1);
}
return count;
}
算法思路:每次 for 循环,都将 num 的二进制中最右边的 1 清除。
为什么 n &= (n – 1) 能清除最右边的1呢?
因为从二进制的角度讲,n 相当于在 n - 1 的最低位加上1。
举个例子:
8(1000)= 7(0111)+ 1(0001)
所以 8 & 7 = (1000)&(0111)= 0(0000),清除了 8 最右边的 1(其实就是最高位的 1,因为 8 的二进制中只有一个1)。
再比如:
7(0111)= 6(0110)+ 1(0001)
所以 7 & 6 = (0111)&(0110)= 6(0110),清除了 7 的二进制表示中最右边的 1(也就是最低位的 1)。
求中点的大小
int mid = L + ((R - L) >> 1); // 中点
等价于
int mid = (L + R) / 2;
为什么要使用第一种方法呢?因为 L + R 如果数特别大,可能会有溢出的情况,这样就会导致结果不正确
// 上面的 mid 为了溢出也可以这样写
int mid = L + (R - L) / 2; // 就是直接加上两数之间的差
// 对上面这种操作简化(图一乐),就成了下面这种右移操作
int mid = L + ((R - L) >> 1);
判断某个数的第 i 位是0 还是 1?
思路:
- 如果第 i 位 与 1 相与 结果为 1 表明第 i 位为 1;
- 如果为 0 表明第 i 位为 0
//获取 整数 num 的第 i 位的值
private static boolean getBit(int num, int i)
{
return ((num & (1 << i)) != 0); //true 表示第i位为1,否则为0
}
1 左移 i 位后,得到一个数,这个数只有第 i 位为 1,其它位都为 0
快速找到右边第一位 1 的位置
例如快速找到这个 eor 右边第一位 1 的位置
// 快速的方法(~eor + 1 就是求补码)
int rightOne = eor & (~eor + 1);
// 等价于下面这种
int rightOne = 0;
while ((eor & rightOne) == 0)
rightOne = rightOne << 1; //找到从右边开始第一个1的位置
将第 i 位设置为 1
思路:
- 第 i 位与 0 “或”,值不变。
- 第 i 位与 1 “或”,变成 1。
因此,我们的目标是 num 与 一个第 i 位值为 1,其它位的值都为 0 的数相 “或”
//将 整数 num 的第 i 位的值 置为 1
private static int getBit(int num, int i)
{
return (num | (1 << i));
}
将第 i 位设置为 0
思路:
- 第 i 位和 0 “与”,第 i 位就变成了 0。
- 其它位 都与 1 “与”,其它位保持不变。
这样,就只把第 i 位清 0 了
//将 整数 num 的第 i 位的值 置为 1
private static int getBit(int num, int i)
{
int mask = ~(1 << i); //111011
return (num & (mask));
}
注:~ 按位取反运算符翻转操作数的每一位,即 0 变成 1,1 变成 0。
例如:A 的值为 60,~A 得到 -61,即 1100 0011