LeetCode第七题: 整数反转(Java)

博客探讨了如何在不使用64位整数的情况下,反转一个32位有符号整数,并处理反转过程中的边界问题。通过数学方法实现数字反转,避免字符串转换,同时分析了不同情况下的边界条件,包括正数、负数和零的情况。文章还提到了两种不同的Java实现方式,并指出在处理溢出时的微妙之处。最后,提到了使用字符串反转方法的简便性,但指出这种方法可能不适用于所有语言。

题目: 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [-2^{31},( 2^{31} )− 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

示例:

输入:x = -123
输出:-321

输入:x = 123
输出:321

题目解读:

题目比较容易理解,数字反转和注意边界问题。

不能使用long类型(环境不允许存储 64 位整数

这种纯数字问题,首先考虑数学方法,不要直接就转换为字符串再转整形。

方法一:官方方法

重复「弹出」x 的末尾数字,将其「推入」到rev的首位,直至 x 为 0。

可以不断取模乘以十,再相加。

基本思路
//x为需要反转的数字,pop为每次取模的数字(也就是每次x的最后一位数字)
int pop = x % 10;
x /= 10;
//rev存储反转后的数字
rev = rev * 10 + pop;

接下来只需要考虑边界问题

Integer.MIN_VALUE \leq rev * 10 + pop\leqInteger.MAX_VALUE

为什么不能直接 Integer.MIN_VALUE \leq rev \leqInteger.MAX_VALUE ?

如果 rev = rev * 10 + pop ,后再进行边界判断。在rev = rev * 10 + pop给rev赋值时,可能rev符合边界,但rev * 10 + pop超出边界范围,rev已经存储不下这么大的数字,再强给rev赋值则会出现超出边界被截去。所以要在给rev赋值前进行边界判断。

Integer.MIN_VALUE \leqrev * 10 + pop \leq Integer.MAX_VALUE

1)情况一,x>0   

分析 rev * 10 + pop \leqInteger.MAX_VALUE  

Integer.MAX_VALUE = 2^{31}−1=2147483647 = (MAX / 10) *10 +(MAX % 10)

                                                                      = (MAX / 10) *10 + 7

rev * 10 + pop \leq (MAX / 10) *10 + 7  

移项易得:

(rev - (MAX / 10) )* 10  \leq  7 - pop

讨论不等式成立条件:

rev > (MAX / 10) 时, 左边必大于等于10,pop为 0 至 9,所有不等式必不成立

rev = (MAX / 10) 时, 当 pop \leq  7  ,不等式成立。

rev < (MAX / 10) 时, 左边必小于等于-10 ,pop为 0 至 9,所有不等式必成立

当 rev = (MAX / 10) 时,若还能进入下次循环,则说明 x 的位数与 MAX / 10 位数相同,且这时rev推入的是最后一位数字,也就是x的最高位。由题 x 不超过 Integer.MAX_VALUE ,x最高位只能是1或者2。  pop \leq  7的这个条件必满足,所以 rev = (MAX / 10) 时,不等式恒成立。

得,

当x > 0时,约束条件 rev  \leq  (MAX / 10)。

2)情况2,x < 0  

分析,全部转为正数处理最后加负号。rev * 10 + pop \leq  Integer.MIN_VALUE  

Integer.MIN_VALUE = (MIN / 10) *10 + 8

pop - 8 \leq (( MIN/10 ) - rev )* 10

讨论不等式成立条件:

rev > (MIN / 10) 时, 右边必小于等于 -10 ,pop为 0 至 9,所有不等式必不成立

rev = (MIN / 10) 时, pop \leq 8

rev < (MIN / 10) 时,右边必大于等于  10,pop为 0 至 9,所有不等式必成立

和 x > 0 同理,最后推的数字只能是1或2,所以rev = (MIN / 10) 时, pop \leq 8,恒成立。

得 rev  \leq  (MIN / 10),加负号还原, (MIN / 10)  \leq  rev。

3)情况3,x = 0 时

rev = 0,写代码时作为特殊情况,直接返回0。

综上所得 (MIN / 10)  \leq  rev \leq  (MAX / 10)

public static int reverse(int x) {
        //当 x = 0 时,不进入循环直接返回 0
        int rev = 0;
        while (x != 0) {
            //每次推入后,判断。符合条件的证明 rev 无法再推入数字
            if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10){
                return 0;
            }
            //pop 每次推入的数字
            int pop = x % 10;
            //rev 反转后的数字
            rev = pop + rev * 10;
            //此x 为 推出尾位数字的 x
            x = x / 10;
        }
        return rev;
    }

评论区看到一个方法用    if ((rev * 10) / 10 != rev)  判断是否可以继续推入数字。

    public static int reverse(int x) {
        //当 x = 0 时,不进入循环直接返回 0
        int rev = 0;
        while (x != 0) {
                // 若 rev * 10 时已经溢出,rev * 10就会变化,而不是尾位加个 0。这时它再 /10 一定不在和原 rev相等。
                //314748364 再 *10 已超出边界,314748364*10 打印是 -1147483656
            if ((rev * 10) / 10 != rev) {  //溢出
                return 0;

            }
            //pop 每次推入的数字
            int pop = x % 10;
            //rev 反转后的数字
            rev = pop + rev * 10;
            //此x 为 推出尾位数字的 x
            x = x / 10;
        }
        return rev;
    }

这种方法也只限于Java,rev*10 超出边界后会变化,而不是乘以十。

例如:314748364 * 10 时超出边界,打印结果是 -1147483656

方法二:官方方法改良

减少最后只能推入1或2的推导,把rev = (MAX / 10) , pop \leq  7 和 rev = (MIN / 10) , pop \leq 8写到代码中。

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

    }

基本和方法一原理一样。

方法三:转为字符串

字符串有个 reverse() 方法可以直接使用

原理简单,也是最容易想到的

    public static int reverse(int x) {
        try {
            boolean flag = true;
            if (x < 0) {
                flag = false;
            }
            StringBuilder str = new StringBuilder(Integer.toString(Math.abs(x)));
            str = str.reverse();
            String s = str.toString();
            //当转换超出边界时,就会报异常。
            x = flag ? Integer.parseInt(s) : -Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return 0;
        }
        return x;
    }

总结

数字方面的问题,优先考虑用数学方法化简。而不要直接转为字符串操作。

本来过年时候第十题就写完了,本打算屯着,年后不出差稳定了,每周写一两篇。奈何有要继续出差最近刚忙完,不需要天天加班了。可以慢慢调整生活和学习了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值