题目描述; 请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
- 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
- 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
- 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示:
- 本题中的空白字符只包括空格字符 ’ ’ 。
- 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
算法分析;
- 去掉前导空格
- 再是处理正负号
- 识别数字,注意越界情况
java语言实现
public class solution{
public int myAtoi(String str)
{
char [] chars=str.toCharArray();
int n=chars.length;
int id=0;
while(id<n&&chars[id]==' ')
id++;
if(id==n)
return 0;
boolean negative=false;
if(chars[id]=='-')
{
negative=true;
id++;
)
else if(chars[id]=='+'){
id++;
}
else if(!Character.isDigit(chars[id]){
return 0;
}
int ans = 0;
while (idx < n && Character.isDigit(chars[idx])) {
int digit = chars[idx] - '0';
if (ans > (Integer.MAX_VALUE - digit) / 10) {
// 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
// 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
return negative? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
ans = ans * 10 + digit;
idx++;
}
return negative? -ans : ans;
}
}
c语言实现,思想同上
int myAtoi(char * str) {
char * chars;
chars=str;
int n = strlen(chars);
int idx = 0;
while (idx < n && chars[idx] == ' ') {
// 去掉前导空格
idx++;
}
if (idx == n) {
//去掉前导空格以后到了末尾了
return 0;
}
int negative = 0;
if (chars[idx] == '-') {
//遇到负号
negative = 1;
idx++;
} else if (chars[idx] == '+') {
// 遇到正号
idx++;
} else if ((chars[idx]<48&&chars[idx]>57)) {
// 其他符号
return 0;
}
int ans = 0;
while (idx < n && (chars[idx]>=48&&chars[idx]<=57)) {
int digit = chars[idx] - '0';
if (ans > (2147483647 - digit) / 10) {
// 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
// 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
return negative? -2147483648 :2147483647 ;
}
ans = ans * 10 + digit;
idx++;
}
return negative? -ans : ans;
}
官方中有一种更为高级的算法方法,这里我也讲述一下。
方法二:自动机
字符串处理的题目往往涉及复杂的流程以及条件情况,如果直接上手写程序,一不小心就会写出极其臃肿的代码。
因此,为了有条理地分析每个输入字符的处理方法,我们可以使用自动机这个概念:
我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题。
本题可以建立如下图所示的自动机:
首先给出leetcode给出的自动机的概念
我们的程序在每个时刻有一个状态s,每次从序列中输入一个字符c,并根据字符c 转移到下一个状态s’。这样,我们只需要建立一个覆盖所有情况的从s与c映射到s’的表格即可解决题目中的问题。
如果有小伙伴不理解啥是自动机的话?,可以参考下面的自动机解说
状态分析
字符串str中的每一个字符,都有可能是下面四种类型中的一种。
- 空格字符(Space)
- 正负号 ±(sign)
- 字符串型的数字(Number)
- 除以上三种之外的任何情况(Other)
阶段分析
如果想要把字符串转换为整数,必然会经历四个阶段:
- 开始转换 (start)
- 判断正负(sign)
- 生成数值(in_number)
- 结束转换(end)
生成自动机
这一自动机将状态和阶段巧妙的结合在一起。
下面来看一个表格
每一行代表不同的执行的阶段
- 第0行:开始转换阶段
- 第1行:判断正负阶段
- 第2行:生成数值阶段
- 第3行:结束转换阶段
每一列代表不同的字符类型 - 第0列:字符为空格
- 第1列:字符为正、负号
- 第2列:字符为字符型数值
- 第3列:字符为其他形式
由行列确定的坐标,表示下一个字符所处的执行阶段。
例如下面给出leetcode官方例子:“ -42”。
转换流程如下
- 开始,必然是start阶段,所以是第0行,坐标为[0,?]
- 第一个字符是空格,找到空格所在的列,坐标为[?,0]
- 结合行列,坐标是[0,0],发现将为下一个字符还是执行start阶段
- 所以第二个字符还是从第0行开始,即[0, ?]
- 第二个字符是空格,空格的列数是[?, 0]
- 所以第三个字符的还是执行start阶段([0, 0])
- …(空格的分析不再赘述)
- 发现字符是负号-,而此时是在第0行(之前空格的原因),所以坐标是[0, 1],
- 那么可以下一个字符的执行阶段是signed,即第1列([1, ?])
- 接下来的字符是字符型的4,则列数是[?, 2]
- 所以坐标确定为[1, 2],则下一个字符的执行阶段是in_number,即[2, ?]
- 这次的字符还是字符型(2),则依旧定位到[?, 2],则下一个字符执行in_number阶段
- 没有字符了,遍历结束
- 依据负号和数值,得出转换结果为-42
java语言实现;
class Solution {
class Automation{
String state="start";
Map<String,String[]> map;
public int sign=1;
public long ans=0;
public Automation()
{
map=new HashMap<>();
map.put("start", new String[]{"start","signed","innum","end"});
map.put("signed", new String[]{"end","end","innum","end"});
map.put("innum",new String[]{"end","end","innum","end"});
map.put("end", new String[]{"end","end","end","end"});
}
public int get_col(char c)
{
if (c == ' ') return 0;
if (c == '+' || c == '-') return 1;
if (c >= '0' && c <= '9') return 2;
return 3;
}
public void get(char c)
{
state=map.get(state)[get_col(c)];
if(state.equals("innum"))
{
ans=ans*10+c-'0';
if(sign==1)
ans=Math.min(ans, Integer.MAX_VALUE);
else
ans=Math.min(ans, -(long)Integer.MIN_VALUE);
}
else if(state.equals("signed"))
{
sign=c=='+'? 1: -1;
}
}
}
public int myAtoi(String str) {
Automation automation=new Automation();
char c[]=str.toCharArray();
for(char ch:c)
{
automation.get(ch);
}
return automation.sign*((int)automation.ans);
}
}
如果对这段代码看的不是很懂的话,下面有一个通俗的表达方式
if(state.equals("innum"))
{
if (ans > Integer.MAX_VALUE/10 || (ans== Integer.MAX_VALUE / 10 && c-'0'> 7)) ans= Integer.MAX_VALUE;
else if (ans < Integer.MIN_VALUE/10 || (ans== Integer.MIN_VALUE / 10 &&-(c-'0')< -8)) ans=Integer.MIN_VALUE;
else
{
if(sign==1)
ans=ans*10+c-'0';
else
{
ans=ans*10-(c-'0');
}
// if(sign==-1&&count==0)
// {
// ans=-ans;
// }
}
}
最后返回的也不同
return ((int)automation.ans);
C语言实现自动机
int myAtoi(char * str){
int len = strlen(str);
if(len == 0)return 0;
int number = 0;
int sign = 1;
enum states {
start,
_signed,
in_number,
end
};
enum states cur_state = start;
enum states state[4][4] = {{start, _signed, in_number, end},
{end, end, in_number, end},
{end, end, in_number, end},
{end, end, end, end}};
for(int i = 0; i < len; ++i){
if(cur_state == end){
break;
}
enum states ff = end;
if(str[i] == ' '){
ff = start;
}
else if(str[i] == '+' || str[i] == '-'){
ff = _signed;
}
else if(str[i] >= '0' && str[i] <= '9'){
ff = in_number;
}
if((cur_state = state[cur_state][ff]) == in_number){
if(number < INT_MAX / 10 || ((number == INT_MAX / 10) && (str[i] - '0' < 8))){
number = number * 10 + (str[i] - '0');
}
else {
return (sign == 1 ? INT_MAX : INT_MIN);
}
}
else if(cur_state == _signed){
if(str[i] == '-'){
sign = -1;
}
}
}
number *= sign;
return number;
}
最后的最后分享一下关于大佬的javascript解法,解法超级简单,简单到我都要wc了。
话不多说了,直接参考戳这里