这道题,官方解法是极为漂亮,很有助于开拓视野,理解互联网技术的本质。
但是并不会一下子就让你醒悟,而是在以后的某一天,你会恍然大悟的想到今天这道题,心中不由感叹“原来如此!”
题目链接
一般解法
class Func2 {
public int myAtoi(String s) {
int i = 0;
int len = s.length();
int sign = 1;
int res = 0;
// 如果字符串前导位置为空格,循环到有数据的那一个位置
while (i < len && s.charAt(i) == ' ') i ++;
int start = i; // 记录一下当前之后的所有数据的开始位置
for (; i < len; i ++) {
int c = s.charAt(i);
// 判断在开始位置,符号是否为+
if (i == start && c == '+') sign = 1;
else if (i == start && c == '-') sign = -1;
// 判断是数字
else if (Character.isDigit(c)){
int num = c - '0';
// 如果是数字,其他不用考虑,只需要考虑两种超限的情况
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10))
return Integer.MAX_VALUE;
else if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && -num < Integer.MIN_VALUE % 10))
return Integer.MIN_VALUE;
res = res * 10 + sign * num;
} else {
// 如果有一次循环既不是数字,也不是+-,那么立刻退出循环,并返回当前res中存储的值
break;
}
}
return res;
}
}
这没什么可说的,重点是下面的解法,有着真正互联网技术领域的积淀精华在里面
重点解法
public class Atoi {
/*
字符串-中
https://leetcode-cn.com/problems/string-to-integer-atoi/
请你来实现一个myAtoi(string s)函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−2^31, 2^31− 1] ,需要截断这个整数,使其保持在这个范围内。
具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31− 1 的整数应该被固定为 2^31− 1 。
返回整数作为最终结果。
注意:
本题中的空白字符只包括空格字符 ' ' 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
*/
public static int myAtoi(String str) {
Automaton automaton = new Automaton();
int length = str.length();
for (int i = 0; i < length; i ++)
automaton.get(str.charAt(i));
return (int) (automaton.sign * automaton.ans);
}
public static void main(String[] args) {
String input = " -2423a ";
int output = myAtoi(input);
System.out.println(output);
}
}
class Automaton {
public int sign = 1;
public long ans = 0;
private String state = "start";
private Map<String, String[]> table = new HashMap<String, String[]>() {{
put("start", new String[]{"start", "signed", "in_number", "end"});
put("signed", new String[]{"end", "end", "in_number", "end"});
put("in_number", new String[]{"end", "end", "in_number", "end"});
put("end", new String[]{"end", "end", "end", "end"});
}};
public void get(char c) {
state = table.get(state)[get_col(c)];
if ("in_number".equals(state)) {
ans = ans * 10 + c - '0';
ans = sign == 1 ? Math.min(ans, (long) Integer.MAX_VALUE) : Math.min(ans, -(long) Integer.MIN_VALUE);
} else if ("signed".equals(state))
sign = c == '+' ? 1 : -1;
}
private int get_col(char c) {
if (c == ' ') return 0;
if (c == '+' || c == '-') return 1;
if (Character.isDigit(c)) return 2;
return 3;
}
}
分析
理解这张图是关键,但要想理解,在没有想通之前还是要调试代码能够起到辅助理解的作用
结合解题逻辑对上图开展讲解(下面的(m,n)的意思是第m行第n列):
- 先看(2,2),这格的意思是你传进来了一个字符串,如果第一个字符是空格,那么就往前进一步,但还是会找有没有空格
- 找到什么时候呢?
- 直到遇到+/-符号,这个时候,任务改变啦,符号有了,接下来就是找数字了。
- 只要找到的是数字就继续找,一旦碰到其他的字符(不是数字/+/-/‘ ’)就结束了。
之后就是延伸了。上面说的是基础,其中遇到的种种情况最后就汇总成上表了。
随便找一行举个例子,第三行吧
- 现在是找到数字了,那么他会继续往下走(这个前提大家应该都能理解吧)
- 一旦碰到空格,直接返回
- 一旦碰到+/-,直接返回
- 一旦碰到数字,则是继续
- 一旦碰到其他字符,直接返回
要我看,这道题解的理解关键在于调试一下代码,就可以了。
很棒的一道题,解题思想是及其值得学习的。