🚀 手写 atoi:把字符串稳稳地转成整数

1 阅读3分钟

LeetCode 第 8 题:String to Integer (atoi)
是一道看起来简单,但细节极多的经典题目。

这道题并不考复杂算法,而是非常考验:

  • 规则拆解能力
  • 边界条件意识
  • 对整数溢出的理解

本文将围绕一份标准 Java 实现,系统讲清楚这道题的解题思路与设计细节。


一、题目规则拆解

题目要求我们模拟 C/C++ 中的 atoi 行为,将字符串转换为 32 位有符号整数。

转换规则如下:

  1. 忽略字符串前导空格
  2. 可选的正负号 +-
  3. 读取连续的数字字符
  4. 遇到非数字字符立即停止解析
  5. 若数值超出 int 范围,需要进行截断
  6. 若无法转换,返回 0

重点在于:不是调用库函数,而是完整手写解析逻辑。


二、推荐实现代码(核心版本)

class Solution {
    public int myAtoi(String s) {
        int i = 0;
        int n = s.length();
        int sign = 1;
        int res = 0;

        // 跳过前导空格
        while (i < n && s.charAt(i) == ' ') {
            i++;
        }

        // 处理正负号
        if (i < n && (s.charAt(i) == '+' || s.charAt(i) == '-')) {
            sign = s.charAt(i) == '-' ? -1 : 1;
            i++;
        }

        // 读取数字
        while (i < n && Character.isDigit(s.charAt(i))) {
            int digit = s.charAt(i) - '0';

            // 溢出判断
            if (res > Integer.MAX_VALUE / 10 ||
               (res == Integer.MAX_VALUE / 10 && digit > 7)) {
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }

            res = res * 10 + digit;
            i++;
        }

        return res * sign;
    }
}

三、为什么要先跳过空格?

while (i < n && s.charAt(i) == ' ') {
    i++;
}

这是为了处理类似以下输入:

"   -42"

如果不跳过空格,后续逻辑会直接失败。


四、为什么符号要单独处理?

int sign = 1;
if (s.charAt(i) == '-') sign = -1;

这是一个非常重要的设计点:

  • 数字解析阶段只处理“无符号整数”
  • 正负信息由 sign 单独控制

这样做的好处是:

  • 简化数值构建逻辑
  • 为后续统一的溢出判断打下基础

五、digit = s.charAt(i) - '0' 的意义

int digit = s.charAt(i) - '0';

这是字符转整数的标准写法。

原理是 ASCII 编码连续:

'0' -> 48
'1' -> 49
...
'9' -> 57

因此字符减 '0',即可得到对应的数值。


六、溢出判断为什么要提前?

核心代码如下:

if (res > Integer.MAX_VALUE / 10 ||
   (res == Integer.MAX_VALUE / 10 && digit > 7)) {
    return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}

为什么不能先计算再判断?

因为一旦执行:

res = res * 10 + digit;

如果发生溢出,res 本身已经不可信了。

正确做法必须是在乘 10 之前判断是否会溢出。


七、为什么只判断上限,不判断下限?

这是这道题中最容易被忽略、但最巧妙的设计点。

原因如下:

  1. res 始终为非负数

  2. 负号由 sign 控制

  3. 一旦发生正数上溢:

    • 正号 → 返回 Integer.MAX_VALUE
    • 负号 → 返回 Integer.MIN_VALUE

也就是说:

正数上溢,本质上已经覆盖了负数下溢的情况。


八、示例说明

输入:

"-2147483649"

执行过程:

  • 解析符号,sign = -1
  • 数字解析到 214748364
  • 下一位数字为 9
  • 触发溢出判断
  • 返回 Integer.MIN_VALUE

结果完全符合题意。


九、为什么不直接用 long?

确实可以使用 long 来规避溢出,但不推荐:

  • 偏离题目考察重点
  • 面试时容易被追问
  • 学不到真正的边界控制能力

这道题的价值,恰恰在于用 int 写对溢出判断


十、这道题真正考察的能力

  • 字符串解析能力
  • 状态管理与流程控制
  • 整数边界与溢出意识
  • 代码鲁棒性设计

这不是一道算法题,而是一道非常典型的工程题。


总结

atoi 的难点不在转换,而在于规则拆解与边界控制。
当你能从容写出这道题时,说明你已经对整数安全和输入解析有了扎实理解。