在平时工作中, 我们很少直接使用二进制和位运算符, 但是涉及到数字或者标识相关的业务,如果能用上二进制,定能为代码增色不少!
使用场景:
1、比如 HashMap 、Disruptor里面取模 代替除法效率更高
2、布隆过滤器的使用也是巧用二进制和hash,以减少内存的使用;
3、标识业务的状态,以减少内存的使用,比如ZooKeeper的ALL权限:
5、开源snowflake雪花算法也是巧用位操作以达到高效生成ID的效果;
4、DP等动态规划的状态压缩很多场景也可以使用二进制进行压缩,以减少内存的使用
... ...
加解密、hash等算法中也是经常使用到,所以掌握二进制算法是迈向高级研发的重要一步,下面来简单介绍一下二进制算法的一些基础知识,希望能达到抛砖引玉的效果;
计算机的二进制操作,主要包括位移(有符号位移、无符号位移)、与、或、异或:
Java中的位操作指定包括:
~ 按位非(NOT) 逐位取反 |
& 按位与(AND) 全1为1, 有0为0; 任何数与0异或都等于0。 |
| 按位或(OR) 有1为1, 全0为0。 任何数与0或都等于原值。 |
^ 按位异或(XOR) 相同为0,相异为1; 任何数与0异或都等于原值。 |
>> 右移 符号位是0补0,是1补1 |
>>> 无符号右移 高位补0 |
< |
注意Java中没有无符号左移。
对java一定要注意int(32位)、long(64位)类型,二进制的长度
1. >> 带符号右移。正数右移高位补0,负数右移高位补1。比如:
4 >> 1,结果是2-4 >> 1,结果是-2-2 >> 1,结果是-1
2. >>>:无符号右移。无论是正数还是负数,高位通通补0.
对于正数而言,>>和>>>没区别。对于负数而言, -2 >>> 1,结果是2147483647(Integer.MAX_VALUE), -1 >>> 1,结果是2147483647(Integer.MAX_VALUE)
所以,要判断两个数符号是否相同时,可以这么干:
return ((a >> 31) ^ (b >> 31)) == 0
3. & ,位与,一般用于将二进制某些特定位置为1 或0
有了上面的基础知识铺垫,可以尝试做以下涉及二进制位操作的题,以加深理解:
1、给定int num 的值,不用除法如何判断奇偶性?
int a = 3;System.out.println( (a & 1) == 0 ? "偶数" : "奇数");
2、给定int a、int b 的值,不用加、减、乘、除 、不用额外第三个参数,如何交换a 、 b 的值?
int a = 10; int b = 20; a = a ^ b; b = a ^ b; a = a ^ b;
2、给定int num 的值,交换其二进制的 第 i 位 和 第 j 位后的值是多少?
/** * 交换 x 值的 二进制表示的 第 i 位 和 第j位 * * @param x * @param i * @param j * @return */ static int swap_bit(int x, int i, int j) { // 先判断第i位 和 第 j 位的值是否相等 // 如果相等,则无需交换,直接返回即可 if (((x >> i) & 1) != ((x >> j) & 1)) { x ^= (1 << i) | (1 << j); } return x; }
3、给定2个 正整数 int a、int b 的值,如何不用加、减、乘、除 计算 a 与 b的乘积?
思路是转换为 二进制 进行计算,应用栈的特性,每次弹出2个计算其和即可,代码如下:
/** * 利用二进制的加法计算2个int类型和 * @param x * @param y * @return */ public int binaryAdd(int x, int y) { int v = 0, advance = 0, r = 1; while (x > 0 || y > 0) { int i = x & 1; int j = y & 1; int b = i ^ j; if (b > 0) { // 不相同 一个0 一个 1 if (advance != 0) { // 有进位 :1+1 = 0 b = 0; } } else { // 相同 if ((i & j) > 0) { // 都是 1 if (advance != 0) { b = 1; } else { b = 0; // 1+1 = 0,需要进位 advance = 1; } } else { if (advance != 0) { b = 1; advance = 0; } else { b = 0; } } } if (b > 0) { b = b << shift(r); v |= b; } r = r << 1; x = x >> 1; y = y >> 1; } // 计算最后一位还有进位时,再进位 if (advance > 0) { v |= (advance << shift(r)); } return v; } /** * 左移动的 位数 * @param r * @return */ private int shift(int r) { int count = 0; while (r > 0) { count++; r = r >> 1; } return count - 1; } /** * 乘法 * @param a * @param b * @return */ int binaryMultipy(int a, int b) { if (a == 0 || b == 0) { return 0; } Stack stack = new Stack<>(); int s = 0; while (b > 0) { if ((b & 1) > 0) { stack.push(a << s); } else { stack.push(0); } b = b >> 1; s++; } while (stack.size() > 1) { int x = stack.pop(); int y = stack.pop(); int z = binaryAdd(x, y); stack.push(z); } return stack.pop(); }