LeetCode 8. 字符串转换整数 (atoi)

LeetCode 8. 字符串转换整数 (atoi) 解析

这个问题要求将字符串转换为 32 位有符号整数,需要处理前导空格、符号位、数字字符以及溢出情况。以下是详细解析:

方法思路

  1. 去除前导空格

    从字符串开头跳过所有空格字符。

  2. 处理符号位

    检查第一个非空格字符是否为 +-,确定结果的符号。

  3. 转换数字字符

    从符号位之后开始,连续读取数字字符(0-9)并转换为整数。遇到非数字字符时停止。

  4. 溢出处理

    在转换过程中检查是否溢出 32 位有符号整数的范围([-2^31, 2^31-1])。

C++ 代码实现

cpp 复制代码
#include <string>
#include <climits>

class Solution {
public:
    int myAtoi(string s) {
        int i = 0;
        int n = s.length();
        
        // 1. 跳过前导空格
        while (i < n && s[i] == ' ') {
            i++;
        }
        
        // 2. 处理符号位
        int sign = 1;
        if (i < n && (s[i] == '+' || s[i] == '-')) {
            sign = (s[i] == '-') ? -1 : 1;
            i++;
        }
        
        // 3. 转换数字并处理溢出
        int res = 0;
        while (i < n && isdigit(s[i])) {
            int digit = s[i] - '0';
            
            // 检查溢出:res * 10 + digit > INT_MAX
            if (res > (INT_MAX - digit) / 10) {
                return (sign == 1) ? INT_MAX : INT_MIN;
            }
            
            res = res * 10 + digit;
            i++;
        }
        
        return sign * res;
    }
};

代码解释

  1. 跳过前导空格

    使用 while 循环将指针 i 移动到第一个非空格字符位置。

  2. 处理符号位

    • 若遇到 +,符号为正;若遇到 -,符号为负。
    • 指针 i 后移一位,跳过符号字符。
  3. 转换数字并处理溢出

    • 遍历后续字符,若为数字(isdigit(s[i])),则转换为整数。
    • 溢出检查 :在累加前检查是否会导致溢出。若 res * 10 + digit > INT_MAX,则根据符号返回 INT_MAXINT_MIN
  4. 返回结果

    将累加结果乘以符号位,得到最终转换结果。

复杂度分析

  • 时间复杂度:O(n),其中 n 是字符串长度。只需遍历一次字符串。
  • 空间复杂度:O(1),只需要常数级额外空间。

示例

情况1:正常转换

输入:s = " -42"

输出:-42
解释

  • 跳过前导空格后,遇到符号 -,符号位设为 -1
  • 后续数字字符为 42,转换为整数 42,乘以符号位得到 -42
情况2:溢出处理

输入:s = "2147483648"(即 2^31

输出:2147483647(即 INT_MAX
解释

  • 转换过程中检测到溢出(214748364 * 10 + 8 > INT_MAX),直接返回 INT_MAX
情况3:包含非数字字符

输入:s = "4193 with words"

输出:4193
解释

  • 遇到非数字字符 ' ' 时停止转换,返回已转换的部分 4193

关键点

  1. 溢出检查

    使用 res > (INT_MAX - digit) / 10 代替直接比较 res * 10 + digit > INT_MAX,避免整数溢出。

  2. 提前终止

    一旦遇到非数字字符,立即停止转换,避免无效处理。

  3. 符号位处理

    符号位仅在第一个非空格字符处生效,其他位置的符号字符会被视为非数字字符而终止转换。

这种方法通过线性扫描和边界检查,高效且安全地完成字符串到整数的转换。

方法二:

cpp 复制代码
#include <string>
#include <climits>
#include <unordered_map>

class Automaton {
private:
    std::string state = "start";
    std::unordered_map<std::string, std::vector<std::string>> table = {
        {"start", {"start", "signed", "in_number", "end"}},
        {"signed", {"end", "end", "in_number", "end"}},
        {"in_number", {"end", "end", "in_number", "end"}},
        {"end", {"end", "end", "end", "end"}}
    };
    
    // 获取字符类型对应的列索引
    int get_col(char c) {
        if (isspace(c)) return 0;
        if (c == '+' || c == '-') return 1;
        if (isdigit(c)) return 2;
        return 3;
    }

public:
    int sign = 1;
    long long ans = 0;
    
    void get(char c) {
        state = table[state][get_col(c)];
        if (state == "signed") {
            sign = (c == '+') ? 1 : -1;
        } else if (state == "in_number") {
            ans = ans * 10 + (c - '0');
            // 处理溢出
            ans = (sign == 1) ? std::min(ans, (long long)INT_MAX) : 
                                std::min(ans, -(long long)INT_MIN);
        }
    }
};

class Solution {
public:
    int myAtoi(string s) {
        Automaton automaton;
        for (char c : s) {
            automaton.get(c);
            if (automaton.state == "end") break;
        }
        return automaton.sign * automaton.ans;
    }
};

自动机(有限状态机)原理详解

自动机(Finite State Machine, FSM)是一种抽象计算模型,它由一组状态和状态之间的转移规则组成。在字符串处理中,自动机特别适合处理需要根据输入字符序列按特定规则转换状态的问题。

一、基本概念

1. 核心组件
  • 状态集合 :自动机所有可能的状态。例如,LeetCode 8 中的 {start, signed, in_number, end}
  • 输入字符集 :所有可能的输入字符。例如,{' ', '+', '-', '0'-'9', 其他字符}
  • 转移函数 :定义状态之间的转换规则。例如,在状态 start 下遇到空格,转移到 start 状态。
  • 初始状态 :自动机的起始状态。例如,start
  • 终止状态 :自动机处理结束的状态。例如,end
2. 工作流程
  1. 从初始状态开始。
  2. 按顺序读取输入字符,根据当前状态和输入字符,通过转移函数切换到下一状态。
  3. 重复步骤 2,直到处理完所有输入或进入终止状态。

二、LeetCode 8 的自动机设计

1. 状态定义
  • start:初始状态,尚未处理任何有效字符。
  • signed :已处理符号位(+-)。
  • in_number:正在处理数字字符。
  • end:遇到非法字符或处理完毕,终止处理。
2. 转移规则

用表格表示状态转移函数:

当前状态\输入字符 空格(' ') 符号(+/-) 数字(0-9) 其他字符
start start signed in_number end
signed end end in_number end
in_number end end in_number end
end end end end end
3. 状态转移图
复制代码
start ──(空格)──→ start
      ├──(符号)──→ signed
      └──(数字)──→ in_number
      └──(其他)──→ end

signed ──(数字)──→ in_number
       └──(其他)──→ end

in_number ──(数字)──→ in_number
          └──(其他)──→ end

end ──(任何)──→ end

三、自动机的实现逻辑

1. 状态转移表的代码表示
cpp 复制代码
std::unordered_map<std::string, std::vector<std::string>> table = {
    {"start", {"start", "signed", "in_number", "end"}},
    {"signed", {"end", "end", "in_number", "end"}},
    {"in_number", {"end", "end", "in_number", "end"}},
    {"end", {"end", "end", "end", "end"}}
};
  • :当前状态。
  • :输入字符类型(空格、符号、数字、其他)。
  • :下一个状态。
2. 输入字符分类
cpp 复制代码
int get_col(char c) {
    if (isspace(c)) return 0;        // 空格
    if (c == '+' || c == '-') return 1;  // 符号
    if (isdigit(c)) return 2;        // 数字
    return 3;                        // 其他
}
3. 状态更新逻辑
cpp 复制代码
void get(char c) {
    state = table[state][get_col(c)];  // 根据当前状态和输入字符更新状态
    if (state == "signed") {
        sign = (c == '+') ? 1 : -1;    // 记录符号位
    } else if (state == "in_number") {
        ans = ans * 10 + (c - '0');    // 累加数字
        // 处理溢出
        ans = (sign == 1) ? std::min(ans, (long long)INT_MAX) : 
                            std::min(ans, -(long long)INT_MIN);
    }
}

四、自动机处理示例

输入" -42"
  1. 初始状态start
  2. 处理字符
    • ' '(空格):start → start,保持初始状态。
    • ' '(空格):start → start
    • ' '(空格):start → start
    • '-'(符号):start → signed,记录符号位 -1
    • '4'(数字):signed → in_number,累加数字 4
    • '2'(数字):in_number → in_number,累加数字 4*10 + 2 = 42
  3. 结果sign * ans = -1 * 42 = -42

五、自动机的优势

  1. 逻辑清晰

    将复杂的条件判断转化为明确的状态转移表,避免嵌套的 if-else 语句,提高代码可读性。

  2. 可维护性强

    当需求变化时(如增加新的状态或转移规则),只需修改转移表,无需大规模重构代码。

  3. 完整性

    自动机强制覆盖所有可能的状态和输入组合,减少遗漏边界条件的风险。

  4. 扩展性

    容易扩展到更复杂的字符串处理问题,如词法分析、语法解析等。

六、应用场景

自动机广泛应用于:

  • 字符串匹配(如正则表达式引擎)
  • 编译原理(词法分析、语法分析)
  • 网络协议解析
  • 游戏状态管理
  • 人工智能中的行为决策

在处理具有明确状态转换规则的问题时,自动机是一种强大且优雅的解决方案。

方法三:

cpp 复制代码
class Solution {
public:
    int myAtoi(string s) {
        stringstream ss(s);
        int n;
        ss >> n;
        return n;
    }
};
相关推荐
祁思妙想16 分钟前
【LeetCode100】--- 1.两数之和【复习回滚】
数据结构·算法·leetcode
薰衣草233317 分钟前
一天两道力扣(2)
算法·leetcode
小鲈鱼-21 分钟前
【LeetCode4.寻找两个正序数组的中位数】二分O(log(m+n))
c++·算法
橘颂TA23 分钟前
【C++】红黑树的底层思想 and 大厂面试常问
数据结构·c++·算法·红黑树
chao_78924 分钟前
二分查找篇——寻找旋转排序数组中的最小值【LeetCode】
python·线性代数·算法·leetcode·矩阵
傻欣26 分钟前
动态规划疑惑总结
算法·动态规划
啊我不会诶37 分钟前
倍增法和ST算法 个人学习笔记&代码
笔记·学习·算法
程序员三藏1 小时前
如何使用Pytest进行测试?
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
你的冰西瓜1 小时前
C++ 中最短路算法的详细介绍
c++·算法·图论·最短路
zstar-_1 小时前
【算法笔记】6.LeetCode-Hot100-链表专项
笔记·算法·leetcode