512,反转二进制位_反转二进制位

Life is just like that sometimes, we’re hoping for a unicorn and we get a goat.

有时候人生就是如此,我们期待的是独角兽,得到的却是山羊。

问题描述

在Java语言中有一个类叫Integer,他是int类型的包装类。这个类里面有很多关于二进制的操作,之前在讲

364,位1的个数系列(一)

385,位1的个数系列(二)

402,位1的个数系列(三)

提到过二进制中1的个数的计算方式



/** * Returns the number of one-bits in the two's complement binary * representation of the specified {@code int} value.  This function is * sometimes referred to as the <i>population count</i>. * * @param i the value whose bits are to be counted * @return the number of one-bits in the two's complement binary *     representation of the specified {@code int} value. * @since 1.5 */public static int bitCount(int i) {  // HD, Figure 5-2  i = i - ((i >>> 1) & 0x55555555);  i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);  i = (i + (i >>> 4)) & 0x0f0f0f0f;  i = i + (i >>> 8);  i = i + (i >>> 16);  return i & 0x3f;}

今天讲的不是二进制中1的个数,而是对二进制进行反转。比如1000的二进制是



00000000 00000000 00000011 11101000


反转的结果就是



00010111 11000000 00000000 00000000


我们先来看一下代码,这是Integer类中的代码



/** * Returns the value obtained by reversing the order of the bits in the * two's complement binary representation of the specified {@code int} * value. * * @param i the value to be reversed * @return the value obtained by reversing order of the bits in the *     specified {@code int} value. * @since 1.5 */public static int reverse(int i) {  // HD, Figure 7-1  i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;  i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;  i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;  i = (i << 24) | ((i & 0xff00) << 8) |    ((i >>> 8) & 0xff00) | (i >>> 24);  return i;}


代码解析

上面代码咋一看有点懵,因为他们要么是十进制要么是16进制,16进制看的不是很明白,如果我们把它转化为二进制就比较容易理解了。



0x55555555的二进制是:01010101 01010101 01010101 01010101 0x33333333的二进制是:00110011 00110011 00110011 00110011 0x0f0f0f0f的二进制是:00001111 00001111 00001111 00001111

我们看到在二进制中

0x55555555是每1对0和1交替出现

0x33333333是每2对0和1交替出现

0x0f0f0f0f 是每4对0和1交替出现。


1,先来看第一行代码

i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;

因为0x55555555在二进制中是0和1交替出现的,(i & 0x55555555)相当于把i的二进制中奇数位(从右边数)不变,偶数位全部变为0,然后再左移一位,相当于把奇数位的值全部变成了偶数位。

(i >>> 1) & 0x55555555是先往右无符号右移一位,然后在和0x55555555进行与运算,相当于把原来偶数位上的值移到了奇数位上。

最后再执行或(|)运算,完美的实现了奇偶位上数值的互换。


我们就用0x55555555来测试一下



int num = 0x55555555;System.out.println("num的二进制表示:");System.out.println(Util.bitInt32(num));System.out.println("第一步计算之后num的二进制表示:");num = (num & 0x55555555) << 1 | (num >>> 1) & 0x55555555;System.out.println(Util.bitInt32(num));

来看一下打印结果



num的二进制表示:01010101 01010101 01010101 01010101 第一步计算之后num的二进制表示:10101010 10101010 10101010 10101010


同理,上面的0x33333333和0x0f0f0f0f分别完成了每4位和每8位之间的前半部分和后半部分的交换。后面的就很好理解了,因为在java语言中int是32位的,上面已经完成了8位之间的交换,后面我们只需要把int类型的二进制分为4份,然后再交换。


总结

上面的代码实现过程不是很难,但是对于初学者估计还是有一定的难度的。我们再来看下下面一段代码



public static int reverse(int var0) {  var0 = (var0 & 1431655765) << 1 | var0 >>> 1 & 1431655765;  var0 = (var0 & 858993459) << 2 | var0 >>> 2 & 858993459;  var0 = (var0 & 252645135) << 4 | var0 >>> 4 & 252645135;  var0 = var0 << 24 | (var0 & '\uff00') << 8 | var0 >>> 8 & '\uff00' | var0 >>> 24;  return var0;}

其实上面代码是一样的,只不过一个都是十进制,一个是十进制和16进制的混合。




截止到目前我已经写了500多道算法题了,为了方便大家阅读,我把部分算法题整理成了pdf文档,目前有800多页,大家可以在公众号中回复关键字“pdf”即可获取下载链接。


如果觉得有用就点个"赞"吧