题目地址:https://leetcode-cn.com/problems/string-to-integer-atoi/java
请你来实现一个 atoi 函数,使其能将字符串转换成整数。git
首先,该函数会根据须要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则以下:正则表达式
若是第一个非空字符为正或者负号时,则将该符号与以后面尽量多的连续数字字符组合起来,造成一个有符号整数。算法
假如第一个非空字符是数字,则直接将其与以后连续的数字字符组合起来,造成一个整数。数据结构
该字符串在有效的整数部分以后也可能会存在多余的字符,那么这些字符能够被忽略,它们对函数不该该形成影响。函数
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不须要进行转换,即没法进行有效转换。工具
在任何状况下,若函数不能进行有效的转换时,请返回 0 。学习
提示:code
本题中的空白字符只包括空格字符 ' ' 。
假设咱们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。若是数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。orm
示例 1:
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
咱们尽量将负号与后面全部连续出现的数字组合起来,最后获得 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,由于它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。所以没法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 所以返回 INT_MIN (−231) 。
解法一
严格来讲这个问题其实没有太多考察算法的内容不涉及数据结构、算法思惟的选取。它是一个数据的效验筛选,更多的像开发场景中的一些原始数据的处理参数的筛选。因此不难但有较多的判断语句涉及一些细节问题须要仔细对比
主体
边界
细节
public int myAtoi(String str) { char[] chars = str.toCharArray(); int n = chars.length; int index = 0; //1.去掉前导空格 while (index < n && chars[index] == ' ') { index++; } //2.符号处理 int sign = 1; if(index < n && chars[index] == '+'){ index++; }else if(index < n && chars[index] == '-'){ sign = -1; index++; } //3.拼接数字 int result = 0; while (index < n && chars[index]-'0' >= 0 && chars[index]-'0' <= 9) { int num = chars[index] - '0'; if (result > (Integer.MAX_VALUE - num) / 10) { // 原本应该是 result * 10 + num > Integer.MAX_VALUE // 可是 *10 和 + num 都有可能越界,因此在上面逆向判断 return sign < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; } result = result * 10 + num; index++; } return result * sign; }
解法二:正则表达式
这样一个筛选串的过程咱们除了本身去去遍历判断,也可使用正则表达式的方式。用到Java类库的相关工具但本质上仍是和解法一是同样的。须要引入java.util.regex包下的Pattern与Matcher
public int myAtoi(String str) { str = str.trim(); Pattern pattern = Pattern.compile("[-+]??[0-9]++"); Matcher matcher = pattern.matcher(str); if (matcher.lookingAt()) { String num = str.substring(0, matcher.end()); try { return Integer.parseInt(num); } catch (NumberFormatException nfe) { return num.charAt(0) == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE; } } return 0; }
解法三:有限状态自动机
正则表达的所匹配的全部字符串构成均可以用有限自动机识别,其实上面解法的每一个过程断定就是一个有限自动机的每一个状态。从去除空格阶段到取符号阶段到数字阶段到结束。和KMP同样均可解决字符串匹配相关问题
状态机里面有 4 个概念:
套到这道题里就是咱们的程序在每一个时刻有一个状态 s
,每次从序列中输入一个字符 c
,并根据字符 c
转移到下一个状态 s'
。这样,咱们只须要创建一个覆盖全部状况的从 s
与 c
映射到 s'
的表格便可解决题目中的问题。
答案中给出的示例图是这样的:
用图表来表示:
我又画了下面一张图和上面两个同义。(0表示状态start,1表示状态signed,2表示状态int_number,3表示状态end,'其余'表示除[空格、正负号、数字]以外的字符)
意思就是说依次输入字符。首先字符是进入状态0(start)若是字符是空格那么下个仍然进入状态0.若是是其余则进入状态3(end)结束处理。那咱们就能够实现这样一个自动机
class Automata{ //下次的状态 private int state=0; //状态表 private int[][] table={{0,1,2,3},{3,3,2,3},{3,3,2,3},{3,3,3,3}}; //字符的四种状况 public int getCharState(char c){ if(c==' ')return 0; if(c=='+'|| c=='-')return 1; if(Character.isDigit(c))return 2; return 3; } int result = 0; //结果 int sign = 1; //符号 public void input(char c){ //经过此次的字符设定下个状态 state=table[state][getCharState(c)]; //state == 0 直接过 if(state==1 &&c=='-') sign=-1; if(state==2){ int num = c - '0'; //除了经过当前状态和字符来判断下个去往状态 //溢出发生时其实也应该把状态设为3(end) if (result > (Integer.MAX_VALUE - num) / 10) { result = sign < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; sign = 1; state = 3; }else result = result * 10 + num; } //state == 3 直接过 } }
而后就是使用咱们的自动机解题啦
public int myAtoi(String str) { Automata auto=new Automata(); char[] chars=str.toCharArray(); for(char c:chars){ auto.input(c); } return auto.sign*auto.result; }
或者state为3这边也直接结束不去掉用空方法
public int myAtoi(String str) { Automata auto=new Automata(); char[] chars=str.toCharArray(); int i = 0; while(i<chars.length && auto.state != 3){ auto.input(chars[]); i++; } return auto.sign*auto.result; }
就这题自己来说是比较简单的,主要是处理细节问题可能不少小伙伴在像解法一的处理过程当中没有组织清晰致使虽然能解出题但有不少代码是冗余的或者重复判断了的地方。最终解法一其实也不过两个if而已没有像传闻中那样说不少ifelse,三个处理第一个空格处理是循环处理,第二符号只有首位因此单个if,第三个数字也是循环处理。所以三个必要的判断两个是while一个是if。最后还加上一个溢出的判断。除此以外这题也有学习到的地方,第一是对正则的回顾,第二是引出有限状态自动机这样一个计算机思想也是等于又温习下离散数学。对于这题没有什么算法思惟理清思路书写下来就是解法一的这样一个过程。所以这一题的意图可能就是让咱们学习到有限状态自动机封装这样一个机器或者规则往这个规则里输入语言串它去给咱们输出相应的结果。