由leetcode的整数反转引发的Integer.reverse源码解读

在看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%的用户。
这里仅是做了一次探究记录,但是效果并不理想,还是官方的好。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值