请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。
科学计数法的数字(按顺序)可以分成以下几个部分:
- 1.若干空格
- 2.一个整数或者小数
- 3.(可选)一个 '
e
' 或 'E
' ,后面跟着一个整数(可正可负) - 4.若干空格
小数(按顺序)可以分成以下几个部分:
- 1.若干空格
- 2.(可选)一个符号字符('+' 或 '-')
-
- 可能是以下描述格式之一:
- 3.1 至少一位数字,后面跟着一个点 '.'
- 3.2 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
- 3.3 一个点 '.' ,后面跟着至少一位数字
- 4.若干空格
整数(按顺序)可以分成以下几个部分:
- 1.若干空格
- 2.(可选)一个符号字符('
+
' 或 '-
') -
- 至少一位数字
- 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))
去查看
示例1
输入:"123.45e+6"
返回值:true
示例2
输入:"1.2.3"
返回值:false
这道题咋一看,有点复杂,case
很多,主要是分析好判断分支,我们可以定义几个变量:
hashNum
: 是否已经有数字hashE
:是否已经有EhasSign
:是否已经有符号hasDot
:是否已经有小数点
首先,初始化当前的索引index =0
,字符串头部的空格需要跳过。
- 循环判断索引是否在有效的范围内:
- 循环判断是否是数字,是数字则更新
hasNum = true
,并且索引后移,直到不是数字的时候,跳出循环。
- 循环判断是否是数字,是数字则更新
- 跳出循环后,需要判断当前的
index
是否合法,不合法直接break - 取出当前索引的字符
c
:- 如果
c
是e
或者E
:- 如果前面已经出现过
E
,或者前面没有数字,直接返回false
- 否则,
hasE
置为true
,其他的置为false
,也就是E后面可以继续出现符号数字和小数点了
- 如果前面已经出现过
- 如果
c
是“+
”或者“-
”:- 前面如果已经出现过数字或者符号或者小数点,都不是合法的
- 否则
hasSign
置为true
,表示符号出现过
- 如果
c
是小数点“.
”- 如果前面已经有小数点或者有E出现了,那么就是非法的,返回
false
- 否则
hasDot
置为true
- 如果前面已经有小数点或者有E出现了,那么就是非法的,返回
- 如果
c
为空格,直接跳出循环 - 否则,直接返回
false
- 如果
- 最后也需要跳过空格
- 最后判断是否合法的条件是:是否到达最后一个字符,并且出现过数字
public boolean isNumeric(String str) {
int size = str.length();
int index= 0 ;
// 默认全部是false
boolean hashNum=false ,hasE=false ,hasSign=false ,hasDot=false;
// 跳过空格
while(index<size&&str.charAt(index)==' '){
index++;
}
while(index<size){
while(index<size&&str.charAt(index)>='0'&& str.charAt(index)<='9'){
index++;
// 表示前面有数字
hashNum = true;
}
// 到末尾直接跳出
if(index==size){
break;
}
char c = str.charAt(index);
if(c=='e'||c=='E'){
// 前面有E或者没有数字在前面
if(hasE||!hashNum){
return false;
}
hasE = true;
// 出现E了后面又可以出现数字了
hashNum = false;
hasSign = false;
hasDot = false;
}else if(c=='+'||c=='-'){
if(hasSign||hashNum||hasDot){
return false;
}
hasSign = true;
}else if(c=='.'){
if(hasDot||hasE){
return false;
}
hasDot =true;
}else if(c==' '){
break;
}else{
return false;
}
index++;
}
// 跳过空格
while(index<size&&str.charAt(index)==' '){
index++;
}
return hashNum &&index==size;
}
C++
代码如下:
class Solution {
public:
bool isNumeric(string str) {
int size = str.length();
int index = 0;
// 默认全部是false
bool hashNum = false, hasE = false, hasSign = false, hasDot = false;
// 跳过空格
while (index < size && str[index] == ' ') {
index++;
}
while (index < size) {
while (index < size && str[index] >= '0' && str[index] <= '9') {
index++;
// 表示前面有数字
hashNum = true;
}
// 到末尾直接跳出
if (index == size) {
break;
}
char c = str[index];
if (c == 'e' || c == 'E') {
// 前面有E或者没有数字在前面
if (hasE || !hashNum) {
return false;
}
hasE = true;
// 出现E了后面又可以出现数字了
hashNum = false;
hasSign = false;
hasDot = false;
} else if (c == '+' || c == '-') {
if (hasSign || hashNum || hasDot) {
return false;
}
hasSign = true;
} else if (c == '.') {
if (hasDot || hasE) {
return false;
}
hasDot = true;
} else if (c == ' ') {
break;
} else {
return false;
}
index++;
}
// 跳过空格
while (index < size && str[index] == ' ') {
index++;
}
return hashNum && index == size;
}
};
还有一种取巧的做法,那就是直接借助正则表达式进行匹配,这并非我自己想出来的,源于牛客网@Maokt
,但是并不太推荐这种解法:
import java.util.*;
import java.util.regex.Pattern;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @return bool布尔型
*/
public boolean isNumeric (String str) {
// write code here
// ^表示开头 $ 表示结尾 java中两个\\ 代表一个\
// * 零次或多次匹配前面的字符或子表达式
// ?零次或一次匹配前面的字符或子表达式
// + 一次或多次匹配前面的字符或子表达式
// [] 字符集。匹配包含的任一字符
// (:? )匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配
String p = "^[+-]?(\\d*\\.\\d+|\\d+(\\.\\d*)?)(?:[eE][+-]?\\d+)?$";
return Pattern.matches(p,str);
}
}
总结一下
这道题,其实本质是状态的切换,最最重要的一点,是 E
出现之后,其实小数点和符号,和数字,都是可以再出现的,可以理解为 E
就是一个分割线。