请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
数值(按顺序)可以分成以下几个部分:
- 若干空格
- 一个小数或者整数
- (可选)一个 'e' 或 'E',后面跟着一个整数
- 若干空格
小数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符('+' 或 '-')
- 下述格式之一: 1. 至少一位数字,后面跟着一个点 '.' 2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字 3. 一个点 '.' ,后面跟着至少一位数字
整数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符('+' 或 '-')
- 至少一位数字
部分数值列举如下:
["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
部分非数值列举如下:
["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
示例 1:
输入:s = "0" 输出:true
示例 2:
输入:s = "e" 输出:false
示例 3:
输入:s = "." 输出:false
示例 4:
输入:s = ".1" 输出:true
提示:
- 1 <= s.length <= 20
- s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,空格 ' ' 或者点 '.' 。
思路:
排除所有错误情况 逻辑上没怎么考算法,所以算法方面没难度,难点在于归纳各种正确的情况
-
. 出现正确情况:只出现一次(即前面没出现过),且出现在 e 或 E 的前面
- 因为如果前面出现过 e/E 再出现. 说明 e/E 后面跟着小数,不符合题意
-
e或 E 出现正确情况:只出现一次(即前面没出现过),且出现前有数字,出现后后面也得有数字
-
+、- 出现正确情况:只能在开头 和 e 或 E 的后一位
解题思路:
- 返回值:通过判断 numFlag 是否为 true 来判断字符串符不符合
- 首先通过不断遍历过程中,如果有不符合上面三点情况的话,直接会返回 false
- 如果遍历完还没发现代码有什么问题,不能掉以轻心,因为此时来到了最后一个特殊情况,即 123e 或 123E
- 因为 e/E 后面必须还得有数字,所以可以在遍历过程中看 e/E 后面是否直接跟着数字 或 出现正负号后还有没有跟着数字,比较繁琐
- 因此我直接选择判断 numFlag 是否等于 true,来判断 e/E 后面还有没有出现过数字
- 方法:在出现 e/E 后,将 numFlag 变为 false,如果后面有数字那就自然而然 numFlag 又变为 true
function isNumber(s: string): boolean {
let i: number,
len: number,
numFlag:boolean = false,
dotFlag:boolean = false,
eFlag:boolean = false;
s = s.trim(); // 去掉首尾空格
len = s.length; // 去掉后再重新计算长度
for (i = 0; i < len; i++) {
// 如果是数字,那么直接将 numFlag 变为 true 即可
if (s[i] >= '0' && s[i] <= '9') {
numFlag = true;
} else if (s[i] === '.' && !dotFlag && !eFlag) {
// 如果是 . 那必须前面还出现过 . 且前面没出现过 e/E,因为如果前面出现过 e/E 再出现. 说明 e/E 后面跟着小数,不符合题意
dotFlag = true;
} else if ((s[i] === 'e' || s[i] === 'E') && !eFlag && numFlag) {
// 如果是 e 或 E,那必须前面没出现过 e/E,且前面出现过数字
eFlag = true;
numFlag = false; // 这一步很重要,将是否出现过数字的 Flag 置为 false,防止出现 123E 这种情况,即出现 e/E 后,后面没数字了
} else if (
(s[i] === '+' || s[i] === '-') &&
(i === 0 || s[i - 1] === 'e' || s[i - 1] === 'E')
) {
// 如果是 +/- 那必须是在第一位,或是在 e/E 的后面
} else {
// 上面情况都不满足,直接返回 false 即可,提前剪枝
return false;
}
}
return numFlag;
}