java算法之简单的Reverse Integer

来源:互联网 发布:mac os 10.11镜像下载 编辑:程序博客网 时间:2024/05/14 21:49

转载自:http://blog.csdn.net/ylyg050518/article/details/48444363

问题描述

原文描述:

Reverse digits of an integer. 
Example1: x = 123, return 321 
Example2: x = -123, return -321 
Have you thought about this? 
 Here are some good questions to ask before coding. Bonus points for you if you have already thought through this! 
 If the integer’s last digit is 0, what should the output be? ie, cases such as 10, 100. 
 Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, then the reverse of 1000000003 overflows. How should you handle such cases? 
  Throw an exception? Good, but what if throwing an exception is not an option? You would then have to re-design the function (ie, add an extra parameter).

大意:题目的主要目的就是反转整型数,反转过程中要注意类似类似10,100等末尾为0的数字,反转之后高位不能出现0,同时要注意int型数字反转后的溢出问题。

思路分析

  单纯操作数字的算法离不开基本运算,加减乘除,求余,位运算,等。对于上述问题,如果你有经验的话,应该很快知道应该会用到求余和整数的除法运算。求余数(%)用于分离并获得低位数字,整除(/)用于获得除了低位外剩下的值。照例,我们先不考率溢出的问题,先给出最简单的算法。

/** 反转整型数,常规方法,不考虑溢出问题*/    public static int reverse1(int n) {        int result = 0;        int flag = (n >= 0) ? 1 : -1;        int num = Math.abs(n);        while (num > 0) {            result = result * 10 + num % 10;            num /= 10;        }        return result * flag;    }

说明:时间复杂度为O(n),空间复杂度O(1),没有考虑溢出的情况。

算法改进

  当考虑到溢出时(实际上,一个鲁棒性强的算法是不可能不考虑边界情况的),整型数n反转后的结果可能超出int类型所能表示最大范围,在Java中,int类型的包装类Integer定义了两个常量,MAX_VALUE(2147483647) 和MIN_VALUE(-2147483648),这两个常量指示了int型所能表示的数字范围,如果数字超过这个范围则不能正确的被表示,因此我们在改进代码的时候应该把溢出判定考虑进去,如果反转的过程中出现溢出,函数应该返回一个错误的状态值(这里返回-1),那么现在的问题是就变成了代码什么时候应该把溢出判定加进去,还有判定溢出的条件是什么的问题。 
  从函数的输入入手,当输入值n本身溢出的时候,代码是不能被编译通过的,所以输入n的溢出可以不用判定。其次,我们在算法中用到了求绝对值的函数abs(int n),稍微仔细一点你会发现,这个函数存在溢出的风险,当n=Integer.MIN_VALUE,预期值应该是-MIN_VALUE(2147483648),但是此时结果却是大于MAX_VALUE溢出了,在JDK文档中,对于abs(int n)函数有这样的说明:如果参数等于Integer.MIN_VALUE 的值(即能够表示的最小负 int 值),那么结果与该值相同且为负。所以这里应该加个溢出判定。 
  继续往下看,在while循环中肯定存在着溢出的风险,但是这个溢出会出现在什么时候呢?很显然,从低位开始的反转操作一开始不会出现溢出,当运算执行到最后一个高位时,溢出的可能便会发生,这个时候,result的不能被贸然赋值,判定条件应该生效,这个判定条件为 
(result > Integer.MAX_VALUE / 10)|| (result == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10),看起来很长不容易,别急,我们好好分析以下(如果你已经知道是怎么回事,后面的分析不用看了)。这个表达式分成两个部分,并用逻辑或(||)连接,第一部分,用来试探比较最大值除以10和result的大小关系,如果result较大的话,不用继续了,反转结果肯定是溢出的,因为下一次运算result必定要乘10,第二部分,如果result值等于最大值除以10,并且此时num的值大于最大值除以10的余数,运算结果result=result*10+num%10的值肯定也要大于极大值MAX_VALUE了.所以我们可以写出下面改进后的代码。

改进后的代码

/*     * 反转整型数,考虑溢出的情况,不考虑输入参数溢出的情况,因为输入参数越界时,编译不通过     */    public static int reverse2(int n) {        int result = 0;        int flag = (n >= 0) ? 1 : -1;        // 如果参数等于 Integer.MIN_VALUE 的值(即能够表示的最小负 int 值),那么结果与该值相同且为负。        int num = Math.abs(n);        if (num == Integer.MIN_VALUE)            return -1;        boolean check = true;        while (num > 0) {            result = result * 10 + num % 10;            num /= 10;            check = result > Integer.MAX_VALUE / 10                    || (result == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10);            // 越界判定条件:num>int极大值/10,或者num等于最大值/10并且剩余最高位数字大于极大值除以10的余数            if (check)                return -1;        }        return result * flag;    }

说明:时间复杂度为O(n),空间复杂度为O(1),考虑了溢出。

利用捕获异常来检测溢出

  利用Java的语言的异常捕获特性,我们还可以有一种方法规避溢出的判定。在Java中,JDK提供基本类型与String类的转化方法,并且StringBuffer类提供了reverse()方法用于反转字符串,在String对象向int型转为的过程中,如果该字符串不能转换为适当格式时,抛出NumberFormatException异常(注意传入数字是负数时,也会发生转化异常,所以需要绝对值处理),如果转化后的int型变量溢出,就会抛出这个异常。以下给出代码。

/*     * 利用捕获异常来处理溢出的情况,这里用到了jdk的字符反转方法     */    public static int reverse3(int n) {        try {            int result = 0;            int flag = (n >= 0) ? 1 : -1;            int num = Math.abs(n);            String str = String.valueOf(num);            str = new StringBuffer(str).reverse().toString();            result = Integer.valueOf(str);            return result * flag;        } catch (NumberFormatException e) {            // 但该字符串不能转换为适当格式时,抛出该异常,注意传入数字是负数时,也会发生转化异常,所以需要绝对值处理            return -1;        }    }

补充说明

  需要注意的是,以上算法的反转操作仅仅限于int型的表示范围之内,如果允许使用更大范围的long来存储int型变量反转后的结果,那么溢出将不会发生。

Demo下载地址

原创粉丝点击