描述

剑指offerJZ20题 表示数值的字符串

请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。

科学计数法的数字(按顺序)可以分成以下几个部分:

1.若干空格
2.一个整数或者小数
3.(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个整数(可正可负)
4.若干空格

小数(按顺序)可以分成以下几个部分:

1.若干空格
2.(可选)一个符号字符(’+’ 或 ‘-‘)
3. 可能是以下描述格式之一:
3.1 至少一位数字,后面跟着一个点 ‘.’
3.2 至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字
3.3 一个点 ‘.’ ,后面跟着至少一位数字
4.若干空格

整数(按顺序)可以分成以下几个部分:

1.若干空格
2.(可选)一个符号字符(’+’ 或 ‘-‘)
3. 至少一位数字
4.若干空格

例如,字符串[“+100”,”5e2”,”-123”,”3.1416”,”-1E-16”]都表示数值。
但是[“12e”,”1a3.14”,”1.2.3”,”+-5”,”12e+4.3”]都不是数值。

提示:
1.1 <= str.length <= 25
2.str 仅含英文字母(大写和小写),数字(0-9),加号 ‘+’ ,减号 ‘-‘ ,空格 ‘ ‘ 或者点 ‘.’ 。
3.如果怀疑用例是不是能表示为数值的,可以使用python的print(float(str))去查看
进阶:时间复杂度O(n) ,空间复杂度O(n)

方法一:正则表达式

思路:根据正则表达式匹配字符来实现。由科学计数法中,’E’或’e’的前面为一个整数或者小数,那我们先用正则表达式实现判断整数或者小数:"\\s*[+-]?((\\d+)|((\\d*\\.\\d+)|(\\d+\\.\\d*)))(\\S)*" 然后再补充上科学计数法的后缀:([Ee][+-]?\\d+)?(\\s)*,最终表达式为: "\\s*[+-]?((\\d+)|((\\d*\\.\\d+)|(\\d+\\.\\d*)))([Ee][+-]?\\d+)?(\\s)*"
代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 import java.util.*;
import java.util.regex.Pattern;


public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @return bool布尔型
* \\s代表空,\\d代表数字, ?代表一次或零次, *代表零次或多次, +代表一次或多次, | 代表或,但要注意,他的使用规则是(a|b)必须带一个小括号, 不是只匹配挨着的两个。
* 思路:先判断整数或小数,因为科学计数法的前面也是整数或者小数,最后加上科学计数法的后位,用?表示有或者没有
*
*/
public boolean isNumeric (String str) {
String patterns = "\\s*[+-]?((\\d+)|((\\d*\\.\\d+)|(\\d+\\.\\d*)))([Ee][+-]?\\d+)?(\\s)*";
return Pattern.matches(patterns, str);
}
}

方法二:遍历

思路:遍历字符串,思路同样是先判断整数或小数,然后再判断科学计数法的后缀,具体逻辑和代码实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.util.*;
public class Solution {

private int pre = 0;

//先过滤掉符号,再进入无符号数字的判断
private boolean integer(String s){
if (pre < s.length() && (s.charAt(pre) == '+' || s.charAt(pre) == '-')) pre++;
return unsigned(s);
}
//判断这个字符的第一个非数字符号前面是否存在数字
private boolean unsigned(String s){
int temp = pre;
while (pre < s.length() && (s.charAt(pre) <= '9' && s.charAt(pre) >= '0')) pre++;
return pre > temp;
}
public boolean isNumeric (String str) {
//判断字符串为空或者为零的情况
if (str == null || str.length() == 0) return false;
//得到第一个字符的下标
while (pre < str.length() && str.charAt(pre) == ' ') pre++;
int lat = str.length() - 1;
//得到最后一个字符的下标的后一位
while (lat >= 0 && str.charAt(lat) == ' ') lat--;
lat++;
//此处判断字符串是否全为空格
if (lat <= pre) return false;
//此处判断在遇到第一个非数字字符前,是否有数字(不区分有无符号)
boolean flag = integer(str);
//当第一个非字符为'.'时,根据小数的逻辑,'.'的前后至少有一处
//含有数字,并且前面的数字可以带符号,后面的数字不能带符号
//pre<lat是防止后面访问越界,如果字串全为数字则经过integer(str)后,pre = str.length(), 后续访问就会越界。
if (pre < lat && str.charAt(pre) == '.') {
pre++;
//此处unsigned(str)和flag位置不能颠倒,比如字符串为"600.0"时,
//flag为真则不会执行unsigned(str),导致pre少加一位,最后会导致
//pre == lat 判断错误
flag = unsigned(str) || flag;
}
//当遇到'e'或'E'时,符号前方可以为小数或者整数(上文已判断),符
//号后方为整数(可以带正负号)
if (pre < lat && (str.charAt(pre) == 'E' || str.charAt(pre) == 'e')) {
pre++;
flag = flag && integer(str);
}
//pre == lat 用于判断遍历是否完全,排除有其他字符的情况
return flag && (pre == lat);

}
}