在看leetcode题时有个整数反转的题,比如将123变为321之类的,溢出返回0,这让我想起了java里的Integer整数类里有个反转方法reverse,就想看看两者是不是一样的?
如果不一样,能否将Integer这种反转方式应用到题目上?是否只有数学方式一个个位数倒置?
Integer.reverse的源码如下:
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;
}
我们来解读下这个源码:
先来假设前提条件:int i = 23;// 0b00010111 ,后面假设bit数为87654321,#表示0, 1是前面,8是后面,0b表示二进制.
-
先看第一行源码:i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555. 拆分下:
0x55555555 : 01010101 01010101 01010101 01010101 (i & 0x55555555) << 1:取奇位数:#7#5#3#1 -> 7#5#3#1# (i >>> 1) & 0x55555555 : 右移一位:#8765432 取奇位数:#8#6#4#2 (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555 :78563412 即运算结果为00101011
-
来看看第二行源码:i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333
0x33333333 : 00110011 00110011 00110011 00110011 (i & 0x33333333) << 2: 4位之中取前两位左移两位:##56##12 -> 56##12## (i >>> 2) & 0x33333333 : 右移两位取前两位:##785634 -> ##78##34 (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333 : 56781234 即 10001110
-
来看看第三行源码:(i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f
0x0f0f0f0f:00001111 00001111 00001111 00001111 (i & 0x0f0f0f0f) << 4 : 取前4位左移4位:####1234 -> 1234#### (i >>> 4) & 0x0f0f0f0f : 右移4位取前4位: ####5678 -> ####5678 (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f : 12345678
-
最后看看第四行源码:(i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24)
0xff00 : 11111111 00000000 (i << 24) : 左移24位:12345678 ######## ######## ######## ((i & 0xff00) << 8):弃掉前8位右移8位:######## ######## ((i >>> 8) & 0xff00): 右移8位弃掉8位:######## ######## (i >>> 24): 右移24位 :######## ######## ######## ######## (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24) : 12345678 ######## ######## ########
总结:我们可以看到,源码是将整数i变成二进制数,然后分成一小块一小块的来完成反转,先相邻的12换成21,
再2个再4个,即有了>>1>>2>>4这样的位移,而且源码是按32位来算的,并不是按个十百千万来反转的,
所以就有这样的结果。
那么这种bit移动能否应用呢,我们可以移动3bit(8进制),4bit(16进制),但是没有10进制的,我就想到把10进制数原样变成16进制的,比如123 => 0x123,这样子就能够做位移了,然而这一步添加了额外的开销,效果并不好。最终还是得做一个个的数字移动,如下:
public static int reverseHex(int x){
int symbol = x < 0 ? -1 : 1;
x = Math.abs(x);
if(x == Integer.MIN_VALUE) return 0;
long y = 0;
while (x != 0){
y = (y << 4) | x%10 ;
x /= 10;
}
y = Long.parseLong(Long.toHexString(y));
return (int)y == y ? symbol * (int)y : 0;
}
执行效果:时间3 ms,java击败了23.96%的用户,内存36.8 MB,击败了88.89%的用户。
这里仅是做了一次探究记录,但是效果并不理想,还是官方的好。