【刷题】整数反转(ReverseInteger)

部门通知又要开始考试了,于是就又要开始刷题了,从今天起,争取在leetcode上刷一道题,目标是不要定的太高,工作日做简单的,周六日做困难的。加油,没事刷些题也是好的,工作用不着,面试还能用呢~就像口罩没想到自己能成为热门年货,未来的事儿谁说的准呢。

题目

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [ 2 31 , 2 31 1 ] [−2^{31}, 2^{31} − 1] 。请根据这个假设,如果反转后整数溢出那么就返回 0。

来源:https://leetcode-cn.com/problems/reverse-integer

解题

思路

这个题目很简单,看到的第一个想法就是输入改成字符串,然后用charAt反着遍历一遍。

看到示例二,想起还有负数,在反着读的时候还要考虑一下首位的问题,这么一来,特殊处理有点麻烦,所以想着只用字符串来表示他的数字部分,符号单拿出来最后添上。

注意中提到要是反转后溢出需要返回值为0,那肯定要用long来缓存这个数,让他随便折腾,最后再和Integer.MAX_VALUEInteger.MIN_VALUE比较一下输出就好了。

于是有了第一版代码

代码

public int reverse(int x) {
    boolean isNegative = false;
    long temp = x;
    if (x < 0) {
        isNegative = true;
        temp = -x;
    }
    String str = String.valueOf(temp);
    StringBuilder sb = new StringBuilder();
    for (int i = str.length() - 1; i >= 0; i--) {
        sb.append(str.charAt(i));
    }
    temp = isNegative ? -Long.valueOf(sb.toString()) : Long.valueOf(sb.toString());

    if (temp > Integer.MAX_VALUE || temp < Integer.MIN_VALUE) {
        return 0;
    }
    return (int) temp;
}

感觉很完美,于是submit了一把,然后啪啪打脸,Line 14: java.lang.NumberFormatException: For input string: "8463847412-"
仔细查一遍代码,在输入值为负数时,我直接用temp=-x进行赋值,一般场景没有问题,但是在Integer的下边界时出现了问题,Integer.MIN_VALUE的数字部分比Integer.MAX_VALUE要大一,直接取负会先按照int进行变换,溢出,再扩大范围赋值给temp,改成temp = -temp后全用例通过。

ps: 我的解法很暴力,浪费了不少空间,只比29%的同学强,有更加节约的方法可以处理这个问题。
看了下leetcode上的正统答案,是纯靠数学思路解决问题的。操作也不难,就是边界设计确实是我没想的到的。具体实现思路如下,将input每次对10求余,然后将余数乘10叠加,就相当于时10进制的左移以为,但是需要考虑溢出问题,然后居然还用到了放大缩小!!!所以有了下面的推导。

int remainder = x % 10;
x = x/10;
int ans = ans * 10 + remainder;

左侧ans最大为IntegerMax,所以上一次剩余的ans最大只能为IntegerMax/10,其实也好理解,就是最大int值长度,最后一位是0,即ans<IntegerMax/10时,随便处理当ans=IntegerMax/10时,就要考虑后面的remainder其最大值为IntegerMax-IntegerMax/10*10=7.
所以有如下代码:

public int reverse(int x) {
    int ans = 0;
    while (x != 0) {
        int remainder = x % 10;
        x /= 10;
        if (ans > Integer.MAX_VALUE/10 || (ans == Integer.MAX_VALUE / 10 && remainder > 7)) return 0;
        if (ans < Integer.MIN_VALUE/10 || (ans == Integer.MIN_VALUE / 10 && remainder < -8)) return 0;
        ans = ans * 10 + remainder;
    }
    return ans;
}

知识点

  1. StringBuilder是非线程安全的,StringBuffer是线程安全的,用StringBuilder是因为他做拼接时效率比较高。
  2. 在做符号操作时,不会进行范围扩展,窄范围向宽范围进行转换时,可直接赋值过去,反之因为会发生精度丢失,需要声明强转。(Java编程思想简要笔记——第三章 操作符
  3. 负数求余怎么做?
    a = q d + r 0 r < d a = qd + r 且0 ≤ r < d 其中 q q 是商, r r 是余数, d d 是除数。所以对于 1 ÷ 10 -1÷10 就可以很快得出 1 = 1 10 + 9 -1=-1*10+9 这个和-1%10的结果是不一样的。
发布了20 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/kiba_zwei/article/details/104304982