描述

题目描述

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

示例
输入:"123.45e+6"
返回值:true
引言

本题有一个方法叫 “确定有限状态自动机”的方法,但还是枚举情况的方法最好理解

知识点:字符串
难度:⭐⭐⭐


题解

解题思路

有一种叫 “确定有限状态自动机” 的方法,但代码过长,也不太好理解,因此最好理解的还是符合大多数人的:枚举各种情况,遍历每个字符,然后根据各种可能出现的情况做出标记

方法一:枚举情况

图解

image-20210628233452771

算法流程:
  • 定义3个布尔类型的标识位,分别表示之前是否遇到过数字、小数点、‘e’或'E'
  • 对转为字符数组的 arr 进行每个字符的遍历,并判断以下几种情况:
    • 1、当前字符是否为 0~9
    • 2、如果是小数点,则小数点之前不能重复出现小数点、或出现‘e’、'E'
    • 3、如果当前字符位 ‘e’ 或 ‘E’,‘e’或'E'前面必须有整数,且前面不能重复出现‘e’或'E'
    • 4、正负号只可能出现在第一个位置,或者出现在‘e’或'E'的后面一个位置
  • 如果满足以上四种情况,则最后根据最后字符是否为数字,返回 true 或 false
Java 版本代码如下:
import java.util.*;

public class Solution {
    // 例子:str = -123.45e+6
    public boolean isNumeric (String str) {
        if(str == null || str.length() == 0) {
            return false;
        }
        // 标记之前是否遇到过数字、小数点、‘e’或'E'
        boolean isNum = false, isDot = false, isE = false; 
        // 删除字符串头尾的空格,转为字符数组
        char[] arr = str.trim().toCharArray();  
        for(int i = 0; i < arr.length; i++) {
            // 判断当前字符是否为 0~9 
            if(arr[i] >= '0' && arr[i] <= '9') {
                isNum = true;
            } else if(arr[i] == '.') { 
                // 小数点之前不能重复出现小数点、或出现‘e’、'E'
                if(isDot || isE) {
                    return false;
                }
                // 标记已经遇到小数点
                isDot = true; 
            } else if(arr[i] == 'e' || arr[i] == 'E') { // 遇到‘e’或'E'
                 // ‘e’或'E'前面必须有整数,且前面不能重复出现‘e’或'E'
                if(!isNum || isE) {
                    return false;
                }
                // 标记已经遇到‘e’或'E'
                isE = true; 
                // 重置isNum,因为‘e’或'E'之后必须接上整数,因为最后返回isNum,因此需要重置
                isNum = false; 
            } else if(arr[i] == '-' || arr[i] == '+') { 
                // 正负号只可能出现在第一个位置,或者出现在‘e’或'E'的后面一个位置
                if(i != 0 && arr[i-1] != 'e' && arr[i-1] != 'E') {
                    return false;
                }  
            } else {
                // 其它情况均为不合法字符
                return false;
            }
        }
        // 最后一位必须是数字
        return isNum;
    }
}
Python 版本代码如下:
class Solution:
    def isNumeric(self , str ):
        cur = 'int' #int e f
        preint = True
        l = len(str)
        for i in range(len(str)):
            if str[i] == '+' or str[i] == '-':
                # 正负号只可能出现在第一个位置,或者出现在‘e’或'E'的后面一个位置
                if i == l-1:
                    return False
                if i != 0 and str[i-1].lower() != 'e':
                    return False
                preint = False
            elif str[i] == '.':
                # 小数点之前不能重复出现小数点、或出现‘e’、'E'
                if i == l-1:
                    return False
                if cur != 'int':
                    return False
                cur = 'f'
                preint = False
            elif str[i].lower() == 'e':
                # ‘e’或'E'前面必须有整数,且前面不能重复出现‘e’或'E'
                if cur == 'e' or i == l-1 or preint != True:
                    return False
                cur = 'e'
                preint = False
            elif str[i].isdigit():
                preint = True
            else:
                return False
        return True
复杂度分析:

时间复杂度 O(N):遍历整个字符串数组,N 为字符串 str 长度
空间复杂度 O(1):用到的都是常量级别的数据类型

总结:遇到明显需要处理多情况的题目,要学会模拟写出多个复杂例子进行条件评估

方法二:暴力

算法流程:
  • 利用函数将字符串转化为数字,若是异常说明该字符串不能表示数字,则进行异常处理,返回false即可。
Java 版本代码如下:
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param str string字符串 
     * @return bool布尔型
     */
    public boolean isNumeric (String str) {
        // write code here
        try{
            Double res=Double.parseDouble(str);
            return true;
        }catch(Exception e){
            return false;
        }
    }
}
复杂度分析:

时间复杂度 O(N):Double.parseDouble() 方法用到 O(N)
空间复杂度 O(1)