编程之法:面试和算法心得(字符串转换成整数)

内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java

题目描述

输入一个由数字组成的字符串,把它转换成整数并输出。例如:输入字符串"123",输出整数123。

 

分析与解法

 

本题考查的实际上就是字符串转换成整数的问题,或者说是要你自行实现atoi函数。那如何实现把表示整数的字符串正确地转换成整数呢?以"123"作为例子:

 

  • 当我们扫描到字符串的第一个字符'1'时,由于我们知道这是第一位,所以得到数字1。
  • 当扫描到第二个数字'2'时,而之前我们知道前面有一个1,所以便在后面加上一个数字2,那前面的1相当于10,因此得到数字:1*10+2=12。
  • 继续扫描到字符'3','3'的前面已经有了12,由于前面的12相当于120,加上后面扫描到的3,最终得到的数是:12*10+3=123。

 

因此,此题的基本思路便是:从左至右扫描字符串,把之前得到的数字乘以10,再加上当前字符表示的数字。

 

思路有了,你可能不假思索,写下如下代码:

public static int strToInt1(String s)
    {
        int n=0;
        for(int i=0;i<s.length();i++)
        {
            n = n*10+Character.getNumericValue(s.charAt(i));
        }
        return n;
    }

显然,上述代码忽略了以下细节:

  1. 空指针输入:输入的是指针,在访问空指针时程序会崩溃,因此在使用指针之前需要先判断指针是否为空。(由于是java不存在空指针异常
  2. 正负符号:整数不仅包含数字,还有可能是以'+'或'-'开头表示正负整数,因此如果第一个字符是'-'号,则要把得到的整数转换成负整数。
  3. 非法字符:输入的字符串中可能含有不是数字的字符。因此,每当碰到这些非法的字符,程序应停止转换。
  4. 整型溢出:输入的数字是以字符串的形式输入,因此输入一个很长的字符串将可能导致溢出。

首先我们处理问题2和3

思路很简单判断首位如果是+或者数字则为正数,如果是-则为负数。如果是其他字符则抛出异常。循环的过程中检查如果不为数字则抛出异常

代码如下

/*
     * 从左至右扫描字符串,把之前得到的数字乘以10,再加上当前字符表示的数字
     * 无法处理超界限输入
     */
    public static int strToInt2(String s) throws Exception
    {
        int n=0;
        if(s.charAt(0)=='+')
        {
            for(int i=1;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    n = n*10+Character.getNumericValue(s.charAt(i));
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return n;
        }
        else if(s.charAt(0)=='-')
        {
            for(int i=1;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    n = n*10+Character.getNumericValue(s.charAt(i));
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return 0-n;
        }
        else if((int)s.charAt(0)>=48&&(int)s.charAt(0)<=57)
        {
            for(int i=0;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    n = n*10+Character.getNumericValue(s.charAt(i));
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return n;
        }
        else {
            throw new Exception("首位只能为+、-或数字");
        }
    }

一般说来,当发生溢出时,取最大或最小的int值。即大于正整数能表示的范围时返回MAX_INT:2147483647;小于负整数能表示的范围时返回MIN_INT:-2147483648。

现在重点看看如何处理溢出

比较n和MAX_INT / 10的大小,即:

  • 若n > MAX_INT / 10,那么说明最后一步转换时,n*10必定大于MAX_INT,所以在得知n > MAX_INT / 10时,当即返回MAX_INT。
  • 若n == MAX_INT / 10时,那么比较最后一个数字c跟MAX_INT % 10的大小,即如果n == MAX_INT / 10且c > MAX_INT % 10,则照样返回MAX_INT。

对于上面第二种方式,先举两个例子说明下:

  • 如果我们要转换的字符串是"2147483697",那么当我扫描到字符'9'时,判断出214748369 > MAX_INT / 10 = 2147483647 / 10 = 214748364(C语言里,整数相除自动取整,不留小数),则返回MAX_INT;
  • 如果我们要转换的字符串是"2147483648",那么判断最后一个字符'8'所代表的数字8与MAX_INT % 10 = 7的大小,前者大,依然返回MAX_INT。

一直以来,我们努力的目的归根结底是为了更好的处理溢出,但上述第二种处理方式考虑到直接计算n 10 + c 可能会大于MAX_INT导致溢出,那么便两边同时除以10,只比较n和MAX_INT / 10的大小,从而巧妙的规避了计算n\10这一乘法步骤,转换成计算除法MAX_INT/10代替,不能不说此法颇妙。

代码如下

/*
     * 从左至右扫描字符串,把之前得到的数字乘以10,再加上当前字符表示的数字
     */
    public static int strToInt3(String s) throws Exception
    {
        int n=0;
        int maxInt = Integer.MAX_VALUE;
        int minInt = Integer.MIN_VALUE;
        if(s.charAt(0)=='+')
        {
            for(int i=1;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    if(n<maxInt/10||(n==maxInt/10&&maxInt%10>Character.getNumericValue(s.charAt(i))))
                    {
                        n = n*10+Character.getNumericValue(s.charAt(i));
                    }
                    else {
                        return maxInt;
                    }
                    
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return n;
        }
        else if(s.charAt(0)=='-')
        {
            for(int i=1;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    if(n>minInt/10||(n==minInt/10&&(0-maxInt%10)>Character.getNumericValue(s.charAt(i))))
                    {
                        n = n*10-Character.getNumericValue(s.charAt(i));
                    }
                    else {
                        return minInt;
                    }
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return n;
        }
        else if((int)s.charAt(0)>=48&&(int)s.charAt(0)<=57)
        {
            for(int i=0;i<s.length();i++)
            {
                if((int)s.charAt(i)>=48&&(int)s.charAt(i)<=57)
                {
                    if(n<maxInt/10||(n==maxInt/10&&maxInt%10>Character.getNumericValue(s.charAt(i))))
                    {
                        n = n*10+Character.getNumericValue(s.charAt(i));
                    }
                    else {
                        return maxInt;
                    }
                }
                else {
                    throw new Exception("非首位必需为数字");
                }
            }
            return n;
        }
        else {
            throw new Exception("首位只能为+、-或数字");
        }
    }

其实代码思路上没有什么难度,主要问题在于各种异常的处理,这也是考验代码是否严谨。例如如果判断是否溢出用N*10这样则不能避免溢出。

 

posted @ 2018-01-03 11:11  icychen  阅读(232)  评论(0编辑  收藏  举报