剑指leetcode—字符串转换整数 (atoi)

题目描述; 请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

  • 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
  • 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
  • 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示:

  1. 本题中的空白字符只包括空格字符 ’ ’ 。
  2. 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

算法分析;

  1. 去掉前导空格
  2. 再是处理正负号
  3. 识别数字,注意越界情况

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中的每一个字符,都有可能是下面四种类型中的一种。

  1. 空格字符(Space)
  2. 正负号 ±(sign)
  3. 字符串型的数字(Number)
  4. 除以上三种之外的任何情况(Other)

阶段分析

如果想要把字符串转换为整数,必然会经历四个阶段:

  • 开始转换 (start)
  • 判断正负(sign)
  • 生成数值(in_number)
  • 结束转换(end)

生成自动机

这一自动机将状态和阶段巧妙的结合在一起。
下面来看一个表格
在这里插入图片描述
每一行代表不同的执行的阶段

  • 第0行:开始转换阶段
  • 第1行:判断正负阶段
  • 第2行:生成数值阶段
  • 第3行:结束转换阶段
    每一列代表不同的字符类型
  • 第0列:字符为空格
  • 第1列:字符为正、负号
  • 第2列:字符为字符型数值
  • 第3列:字符为其他形式
    由行列确定的坐标,表示下一个字符所处的执行阶段。
    例如下面给出leetcode官方例子:“ -42”。
    转换流程如下
  1. 开始,必然是start阶段,所以是第0行,坐标为[0,?]
  2. 第一个字符是空格,找到空格所在的列,坐标为[?,0]
  3. 结合行列,坐标是[0,0],发现将为下一个字符还是执行start阶段
  4. 所以第二个字符还是从第0行开始,即[0, ?]
  5. 第二个字符是空格,空格的列数是[?, 0]
  6. 所以第三个字符的还是执行start阶段([0, 0])
  7. …(空格的分析不再赘述)
  8. 发现字符是负号-,而此时是在第0行(之前空格的原因),所以坐标是[0, 1],
  9. 那么可以下一个字符的执行阶段是signed,即第1列([1, ?])
  10. 接下来的字符是字符型的4,则列数是[?, 2]
  11. 所以坐标确定为[1, 2],则下一个字符的执行阶段是in_number,即[2, ?]
  12. 这次的字符还是字符型(2),则依旧定位到[?, 2],则下一个字符执行in_number阶段
  13. 没有字符了,遍历结束
  14. 依据负号和数值,得出转换结果为-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了。
话不多说了,直接参考戳这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Devin Dever

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值