剑指Offer-55-表示数值的字符串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dawn_after_dark/article/details/82501301

项目地址:https://github.com/SpecialYy/Sword-Means-Offer

题目

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。

解析

预备知识

在一般编程语言中,”.2”,”2.”,”+.2”是合法的数值表示,而”2e”,”e”,”e2”是不合法的数值表示。

思路一

首先观察题目给的样例,什么是合法的数值表示。主要有以下几点要求:

  1. 肯定不含0 - 9, + , -, e, E之外的字符
  2. +,-合法的位置处于第0位,或者在e的下一个位置出现,其他地方都是不合法
  3. e,E只能出现一次,且不能出现在字符串的首部和末尾。
  4. . 只能出现一次,且 . 不能出现在e之后,. 的左右必然有一方是有数字的

根据以上,可以写出如下代码,所有的情况都在注释给出:

    public static boolean isNum(char ch) {
        return ch >= '0' && ch <= '9';
    }

    /**
     * 常规方法
     * @param str
     * @return
     */
    public static boolean isNumeric1(char[] str) {
        if(str == null || str.length == 0) {
            return false;
        }
        boolean decimalPoint = false, e = false;
        for(int i = 0; i < str.length; i++) {
            char ch = str[i];
            if(ch == '+' || ch == '-') {
                /**
                 * +,—出现在末尾,比如"+", "2+", "2e+"
                 * 在没有e出现的时候,就出现在了非0位置,比如"2+3"
                 * 在出现e之后,"+, -"没有紧挨着e出现,比如"2.3e4+5"
                 * 这些都是“+,—”不合法
                 */
                if(i == str.length - 1 || (i != 0 && !e)
                        || (e && str[i - 1] != 'e' && str[i - 1] != 'E')) {
                    return false;
                }
            }else if(ch == '.') {
                /**
                 * 当.  e 出现过后,. 不能再出现了
                 * 当. 为第0位时,如果串本身就为1,即'.'不是数值
                 * 当. 为第0位时,.的下一位必须是数字
                 * 当. 为第最后一位时,.的前一位必须是数字
                 * 当. 位于非首尾时,左右只要一方是数字即可
                 */
                if(decimalPoint || e || (i == 0 && (i == str.length - 1 || !isNum(str[i + 1])))
                                || (i == str.length - 1 && !isNum(str[i - 1]))
                                || (!isNum(str[i - 1]) && !isNum(str[i + 1]))) {
                    return false;
                }
                decimalPoint = true;
            } else if(ch == 'e' || ch == 'E') {
                /**
                 * e只能出现一次,且不能出现在首部和尾部
                 */
                if (e || i == 0 || i == str.length - 1) {
                    return false;
                }
                e = true;
            } else if(ch < '0' || ch > '9') {
                return false;
            }
        }
        return true;
    }

思路二

这个思路来源于剑指Offer上想法,基于分治的思想,也就是说把一个合法的数分成几部分,然后对各个部分进行合法检测。
根据题目可以得出一个合法的数字的组成为:A.BeA或者.BeA。其中A表示可以带有符号的数字,B表示不带符号的数字。我们只需对上述模型中分别进行不同的检验即可。
用一个标志位表示期间检验的合法性,我们首先判断小数点之前是否是可以带有符号的数字,碰到小数点后,判断是否是不可以带符号的数字,若碰到e,则判断之后是不是可以带有符号的数字。我们制定了检验规则,即依次判断是否有’.’和’e’,所以保证了这些符号只出现一次。最后我们判断标志位是否是true且已遍历完整个字符串即可。
可以带符号的数值与不可以带符号检验的代码其实相似,只不过了多了一步判断是否’+ -‘号。

    static int index = 0;
    /**
     * 拆解法,或者称为分治法
     * A.B[e|E]A
     *
     * @param str
     * @return
     */
    public static boolean isNumeric2(char[] str) {
        if(str == null || str.length == 0) {
            return false;
        }
        index = 0;
        boolean result = scanInteger(str);
        if(index < str.length && str[index] == '.') {
            index++;
            //因为.之前可以没有数字,所以用||
            result = scanUnsignedInteger(str) || result;
        }
        if(index < str.length && (str[index] == 'e' || str[index] == 'E')) {
            index++;
            //e的前面和后面必须有数字,要保证e的前面合法,后面也合法,所以采用&&
            result = result && scanInteger(str);
        }
        return result && index == str.length;
    }

    /**
     * 扫描带符号的整数部分
     * @param str
     * @return
     */
    public static boolean scanInteger(char[] str) {
        if(index < str.length && (str[index] == '+' || str[index] == '-')) {
            index++;
        }
        return scanUnsignedInteger(str);
    }


    /**
     * 扫描不带符号的整数部分
     * @param str
     * @return
     */
    public static boolean scanUnsignedInteger(char[] str) {
        int start = index;
        while(index < str.length && str[index] >= '0' && str[index] <= '9') {
            index++;
        }
        return index > start;
    }

此处举个例子帮助理解,比如123.4E-6。

  1. 首先判断是否是可以带符号的数字,符合
  2. 然后遇到小数点,判断之后是否符合数字,符合
  3. 碰到e,判断之后是否是可以带符号的数字,符合
  4. 标志位为true且已遍历,所以合法

总结

如果能够很好的找到合法数字的组成部分,那么就不用像思路一那么复杂了。

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/82501301