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_MAX 或 INT_MIN。
  4. 返回结果 :

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

复杂度分析

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

示例

情况1:正常转换

输入:s = " -42"

输出:-42
解释:

  • 跳过前导空格后,遇到符号 -,符号位设为 -1。
  • 后续数字字符为 4 和 2,转换为整数 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;
    }
};
相关推荐
牧羊人.3331 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn
小郎君6581 小时前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
axe_hu2 小时前
AI产品经理的职业规划与学习路径
人工智能·职场和发展·产品经理·学习方法
JJJennie7772 小时前
MAI Gateway私有化部署全攻略:大模型网关如何接入?附架构图
算法·gateway·ai网关·魔芋ai
東隅已逝,桑榆非晚2 小时前
算法3.双指针法-快乐数
c++·笔记·学习·算法
专注仿真2 小时前
卷积神经网络(CNN)算法详解
人工智能·python·算法·代码·大模型训练
FII工业富联科技服务2 小时前
多智能体如何进入制造现场?从业务协同到数字孪生验证
大数据·人工智能·算法·机器人·制造·数字孪生
≮傷£≯√2 小时前
httpserver_QNetWorkClient
算法
Evand J2 小时前
【PMSM例程11】永磁同步电机(PMSM)基于UKF的转速与转子位置估计。附下载链接
算法·matlab·卡尔曼滤波·永磁同步电机·ukf·pmsm·位置估计
货拉拉技术3 小时前
记忆即智能:重构对话式AI Agent认知基座的四层渐进式记忆与自进化系统
人工智能·算法