LeetCode:65.有效数字
解题步骤构建一个表示状态的图。遍历字符串,并沿着图走,如果到了某个节点无路可走就返false。遍历结束,如走到3/5/6,就返回true,否则返回false。
extend 2 8 10 16进制
/*** 检查一个字符串是否可以表示为一个有效的数字* @param {string} s - 待检查的字符串* @returns {boolean} - 如果字符串是有效的数字表示,则返回true;否则返回false*/
var isNumber = function(s) {// 状态转换图,定义了字符串中每个字符类型在不同状态下如何转换状态const graph = {0: {'blank': 0, 'sign': 1, '.': 2, 'digit': 6},1: {'digit': 6, '.': 2},2: {'digit': 3},3: {'digit': 3, 'e': 4},4: {'digit': 5, 'sign': 7},5: {'digit': 5},6: {'digit': 6, '.': 3, 'e': 4},7: {'digit': 5}};// 初始化状态为0let state = 0;// 遍历字符串中的每个字符,去除首尾空格for (let c of s.trim()) {// 将字符分类为digit、blank、.、e、signif (c >= '0' && c <= '9') {c = 'digit';} else if (c === ' ') {c = 'blank';} else if (c === '.') {c = '.';} else if (c === 'e' || c === 'E') {c = 'e';} else if (c === '+' || c === '-') {c = 'sign';}// 根据当前状态和字符类型转换到下一个状态state = graph[state][c];// 如果转换结果为undefined,说明字符串不是有效的数字表示if (state === undefined) {return false;}}// 如果最终状态是3、5或6,则字符串是有效的数字表示return state === 3 || state === 5 || state === 6;
};// 示例:检查字符串"1E9"是否是有效的数字表示
console.log(isNumber("1E9"));
代码功能详细解释
这段 JavaScript 代码的功能是判断给定的字符串是否表示一个有效的数字。它使用有限状态机(FSM)来处理字符串中的每个字符,并根据预定义的状态转换规则进行状态切换。具体步骤如下:
-
初始化状态机:
- 定义了一个
graph
对象,表示不同状态之间的转换规则。 - 每个状态对应一个对象,键为字符类型(如
digit
,blank
,sign
,.
,e
),值为下一个状态。
- 定义了一个
-
遍历字符串:
- 使用
trim()
方法去除字符串两端的空白字符。 - 遍历处理后的字符串,逐个字符进行分类和状态转换。
- 使用
-
字符分类:
- 数字字符(
0-9
):转换为digit
。 - 空格字符(
blank
。 - 小数点字符(
.
):保持不变。 - 指数字符(
e
或E
):转换为e
。 - 符号字符(
+
或-
):转换为sign
。
- 数字字符(
-
状态转换:
- 根据当前字符和当前状态,在
graph
中查找下一个状态。 - 如果找不到对应的转换规则(即
state === undefined
),则返回false
,表示字符串不是有效数字。
- 根据当前字符和当前状态,在
-
最终状态检查:
- 遍历结束后,检查最终状态是否为合法的终止状态(3, 5, 6)。只有当最终状态为这些状态之一时,才返回
true
,否则返回false
。
- 遍历结束后,检查最终状态是否为合法的终止状态(3, 5, 6)。只有当最终状态为这些状态之一时,才返回
控制流图(CFG)
flowchart TDA[开始] --> B{字符串为空?}B -->|Yes| E[返回 false]B -->|No| C[初始化状态为0]C --> D[去除空白字符]D --> F[遍历字符串]F --> G{字符类型是什么?}G -->|digit| H[转换为 digit]G -->|blank| I[转换为 blank]G -->|.| J[转换为 .]G -->|e 或 E| K[转换为 e]G -->|+ 或 -| L[转换为 sign]H --> M[查找下一个状态]I --> MJ --> MK --> ML --> MM --> N{找到下一个状态?}N -->|No| O[返回 false]N -->|Yes| P[更新状态]P --> Q{遍历结束?}Q -->|No| FQ -->|Yes| R{最终状态合法?}R -->|No| S[返回 false]R -->|Yes| T[返回 true]
关键点说明
-
状态机设计:
- 状态机的核心是
graph
对象,它定义了从一个状态到另一个状态的转换规则。 - 每个状态都有不同的字符类型可以触发状态转换,例如在状态
0
下遇到digit
会转移到状态6
。
- 状态机的核心是
-
字符分类逻辑:
- 通过条件判断将字符分为五类:
digit
、blank
、.
、e
和sign
。 - 这种分类简化了状态转换的复杂度,使得代码更加简洁易读。
- 通过条件判断将字符分为五类:
-
循环遍历与状态更新:
- 使用
for...of
循环遍历字符串中的每个字符。 - 在每次迭代中,根据当前字符和当前状态查找下一个状态,并更新状态变量
state
。 - 如果在任何时刻找不到合适的转换规则,则立即返回
false
。
- 使用
-
最终状态检查:
- 遍历结束后,检查最终状态是否为合法的终止状态(3, 5, 6)。
- 只有当最终状态为这些状态之一时,才认为字符串是一个有效的数字,返回
true
;否则返回false
。
示例解析
以 console.log(isNumber("1E9"))
为例:
- 初始化状态为
0
。 - 去除空白字符后,字符串为
"1E9"
。 - 遍历第一个字符
'1'
:- 类型为
digit
,根据graph
规则从状态0
转移到状态6
。
- 类型为
- 遍历第二个字符
'E'
:- 类型为
e
,根据graph
规则从状态6
转移到状态4
。
- 类型为
- 遍历第三个字符
'9'
:- 类型为
digit
,根据graph
规则从状态4
转移到状态5
。
- 类型为
- 遍历结束,最终状态为
5
,属于合法的终止状态,因此返回true
。
希望这个详细的解释能够帮助你更好地理解这段代码的工作原理。