7441 字
约 24 分钟
1
008.字符串转换整数(atoi)
无标签

008.字符串转换整数(atoi)

作者:沉默王二 发布时间:2024-01-01T20:35:51.148+0800


  • 语雀专栏地址+密码可通过星球置顶帖查看:https://t.zsxq.com/15rEo9Pdu

  • 技术派专栏地址:https://paicoding.com/column/7/8

  • 星球原地址:https://t.zsxq.com/15u5YkmHY

  • 这道题可以说完全重写了,给出两道大家完全都能掌握的题解,并且追加手绘图,大家会非常容易掌握,一道中等难度直接变身 easy 难度。

  • 增加代码注释,这样大家更容易看懂并掌握题解;

  • 增加参考资料,这样大家可以从零基础直接开刷,减轻新手刷题的阻力;

  • 增加手绘图,这样大家更容易理解解题思路;

  • 增加 ACM 输入输出,这样大家可以在 Intellij IDEA 中进行调试;

  • 增加题解模拟,这样大家可以根据题解模拟整个题解过程,看完会有一种“哦哦”恍然大悟的感觉;

  • 图片上传至 OSS 并配合 CDN,提升大家在技术派网站中的阅读体感。

  • 另外,这一版本会作为 PDF 的终稿随后在星球进行发布。

题意

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  • 读入字符串并丢弃无用的前导空格
  • 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  • 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  • 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  • 如果整数数超过 32 位有符号整数范围 [, − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 的整数应该被固定为 ,大于 − 1 的整数应该被固定为 − 1 。
  • 返回最终结果。

示例

输入:s = "42"

第 1 步:"42"(当前没有读入字符,因为没有前导空格) ^第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^第 3 步:"42"(读入 "42",到达末尾) ^解析得到整数 42 。由于 "42" 在 32 位有符号整数范围内,最终结果为 42 。

输入:s = "   -42"

第 1 步:" -42"(读入前导空格,但忽视掉) ^第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数) ^第 3 步:" -42"(读入 "42",到达末尾) ^解析得到整数 -42 。由于 "-42" 在 32 位有符号整数范围内,最终结果为 -42 。

输入:s = "4193 with words"

第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格) ^第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止) ^解析得到整数 4193 。由于 "4193" 在 32 位有符号整数范围内,最终结果为 4193 。

难度

分析 1

其实这道题的描述对于 Java 党来说,实在是不够友好,在 Java 中,我们都喜欢叫“方法”而不是函数,更没有 atoi 函数。

不过,读完这道题目,很容易联想到 Java 当中的 Integer.parseInt() 方法,这个方法就是来完成数字字符串转整数的,对吧?

我之前在《二哥的 Java 进阶之路 》上也带大家分析过该方法的源码,不过 parseInt 方法仅处理了正负的情况,并没有处理空格的情况、字符串中包含字符的情况,不过我们可以在此基础上进行扩展,来完成这道题目。

①、处理空格我们就用 String 的 trim() 方法来完成。

②、接着我们需要把字符串中数字部分找出来,这里我们可以通过 Character.isDigit() 方法来完成。我们在讲**基本数据类型的包装器类型 **时曾讲过这个方法,不知道大家还记得不?

唯一需要注意的是,我们需要考虑正负号的情况,所以我们需要在找到正负号后,再往后找数字。

class Solution {
    if (str == null || str.length() == 0) {            return 0;        }
    str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
    int sign = 1;        int index = 0;
    // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
    // 提取数字        StringBuilder numberStr = new StringBuilder();        while (index < str.length() && Character.isDigit(str.charAt(index))) {            numberStr.append(str.charAt(index));            index++;        }
    // 没有数字        if (numberStr.length() == 0) {            return 0;        }
    // 尝试转换        try {            return Integer.parseInt(numberStr.toString()) * sign;        } catch (NumberFormatException e) {            return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;        }    }}

代码非常简单,先去掉空格,再判断正负,然后遍历字符串,找出数字的部分,最后尝试通过 Integer.parseInt() 方法来转换,如果转换失败,就返回 Integer.MIN_VALUE 或者 Integer.MAX_VALUE。

比较遗憾的是,这样的代码效率不高。

分析 2

效率低的原因在于,我们在找数字的时候,已经通过 Character.isDigit() 方法判断了字符是否为数字,但是我们还是把它存到了 StringBuilder 中,最后再通过 Integer.parseInt() 方法来转换,这样做的话,就有点多此一举了。

我们可以直接在遍历的过程中,就把数字转换出来,这样就不需要再通过 Integer.parseInt() 方法来转换了。

class Solution {
    if (str == null || str.length() == 0) {            return 0;        }
    str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
    int sign = 1;        int index = 0;
    // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
    // 提取数字        int result = 0;        while (index < str.length() && Character.isDigit(str.charAt(index))) {            int digit = str.charAt(index) - '0';            if (result > (Integer.MAX_VALUE - digit) / 10) {                return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;            }            result = result * 10 + digit;            index++;        }
    return result * sign;    }}

while 循环前面都很好懂,我来详细解释一下 while 循环中的代码:

while (index     int digit = str.charAt(index) - '0';
    // 溢出检查:如果当前的 result 大于最大整数减去当前数字除以 10,    // 则表示下一步操作(result * 10 + digit)会导致溢出    if (result > (Integer.MAX_VALUE - digit) / 10) {        // 根据符号返回最大或最小整数        return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;    }
    // 更新结果:将当前数字累加到结果中    result = result * 10 + digit;
    // 移动到字符串的下一个字符    index++;}

- **循环条件**:index < [str.length](http://str.length)() && Character.isDigit(str.charAt(index)) 确保只有在字符串没有结束且当前字符是数字时,循环才继续。
- **字符转数字**:int digit = str.charAt(index) - '0'; 将字符型的数字转换为整型。在 ASCII 码表中,数字字符 '0' 到 '9' 是连续排列的,所以 str.charAt(index) - '0' 能够得到相应的整数值。
- **溢出检查**:if (result > (Integer.MAX_VALUE - digit) / 10) 检查是否即将发生溢出。因为下一步操作是 result * 10 + digit,如果 result 已经大于 (Integer.MAX_VALUE - digit) / 10,则乘以 10 后加上 digit 一定会超出 int 的范围。这里使用 (Integer.MAX_VALUE - digit) / 10 作为阈值来避免在乘法之后才检测溢出,那时可能已经晚了。
- **更新结果**:result = result * 10 + digit; 将当前数字字符的值累加到 result 中。这里的 result * 10 是为了将之前的结果左移一位(即乘以 10),然后加上当前的数字。

这个 while 循环就很精巧,其实就和 parseInt 方法的处理很接近了。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_10.png)

OK,我们再来看一下运行效率。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_11.png)

果然提高了一大截。

### **总结**

如果你之前认真学过 Java 基础的话,比如说去空格,比如说判断字符的正负号,比如说判断字符是否为数字,比如说把一个字符转成数字。

这些知识点我在《二哥的 Java 进阶之路》上都有给大家讲过,比如说:

- **[判断字符是否为数字](https://javabetter.cn/basic-grammar/basic-data-type.html)**
- **[把一个字符转成数字](https://javabetter.cn/basic-grammar/basic-data-type.html#_04%E3%80%81int-%E5%92%8C-char-%E7%B1%BB%E5%9E%8B%E4%BA%92%E8%BD%AC)**

力扣链接:**[https://leetcode.cn/problems/string-to-integer-atoi/](https://leetcode.cn/problems/string-to-integer-atoi/)**

### **一步一个脚印**

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

该题解目前只针对**[二哥编程星球](https://javabetter.cn/zhishixingqiu/)**的球友开放,如果你也想一起打卡学习算法的话,扫下面的优惠券加入我们吧,星球目前 4100+ 人,定价 149 元,从当初的 99 元一路涨上来的,所以早就是优势,别再犹豫了。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_12.png)

(优惠券仅 300 张,数量有限)

一个人可以走得很快,但一群人才能走得更远。如果想去面中大厂的话,LeetCode 几乎是必刷的,二哥的 LeetCode 题解通俗易懂、图文并茂,理解给你举一反三的解题能力,冲鸭。

        
        - 语雀专栏地址+密码可通过星球置顶帖查看:[https://t.zsxq.com/15rEo9Pdu](https://t.zsxq.com/15rEo9Pdu)
- 技术派专栏地址:[https://paicoding.com/column/7/8](https://paicoding.com/column/7/8)
- 星球原地址:[https://t.zsxq.com/15u5YkmHY](https://t.zsxq.com/15u5YkmHY)
- 这道题可以说完全重写了,给出两道大家完全都能掌握的题解,并且追加手绘图,大家会非常容易掌握,一道中等难度直接变身 easy 难度。

- 增加代码注释,这样大家更容易看懂并掌握题解;
- 增加参考资料,这样大家可以从零基础直接开刷,减轻新手刷题的阻力;
- 增加手绘图,这样大家更容易理解解题思路;
- 增加 ACM 输入输出,这样大家可以在 Intellij IDEA 中进行调试;
- 增加题解模拟,这样大家可以根据题解模拟整个题解过程,看完会有一种“哦哦”恍然大悟的感觉;
- 图片上传至 OSS 并配合 CDN,提升大家在技术派网站中的阅读体感。
- 另外,这一版本会作为 PDF 的终稿随后在星球进行发布。

### **题意**

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
- 如果整数数超过 32 位有符号整数范围 [,  − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于  的整数应该被固定为  ,大于  − 1 的整数应该被固定为  − 1 。
- 返回最终结果。

### **示例**

输入:s = "42"

第 1 步:"42"(当前没有读入字符,因为没有前导空格)         ^第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')         ^第 3 步:"42"(读入 "42",到达末尾)           ^解析得到整数 42 。由于 "42" 在 32 位有符号整数范围内,最终结果为 42 。

输入:s = " -42"

第 1 步:"   -42"(读入前导空格,但忽视掉)            ^第 2 步:"   -42"(读入 '-' 字符,所以结果应该是负数)             ^第 3 步:"   -42"(读入 "42",到达末尾)               ^解析得到整数 -42 。由于 "-42" 在 32 位有符号整数范围内,最终结果为 -42 。

输入:s = "4193 with words"

第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)         ^第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')         ^第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)             ^解析得到整数 4193 。由于 "4193" 在 32 位有符号整数范围内,最终结果为 4193 。

### **难度**

中

### **分析 1**

其实这道题的描述对于 Java 党来说,实在是不够友好,在 Java 中,我们都喜欢叫“方法”而不是函数,更没有 atoi 函数。

不过,读完这道题目,很容易联想到 Java 当中的 **[Integer.parseInt()](https://javabetter.cn/basic-grammar/basic-data-type.html#_05%E3%80%81%E5%8C%85%E8%A3%85%E5%99%A8%E7%B1%BB%E5%9E%8B)** 方法,这个方法就是来完成数字字符串转整数的,对吧?

我之前在《**[二哥的 Java 进阶之路](https://javabetter.cn/basic-grammar/basic-data-type.html)**》上也带大家分析过该方法的源码,不过 parseInt 方法仅处理了正负的情况,并没有处理空格的情况、字符串中包含字符的情况,不过我们可以在此基础上进行扩展,来完成这道题目。

①、处理空格我们就用 String 的 trim() 方法来完成。

②、接着我们需要把字符串中数字部分找出来,这里我们可以通过 Character.isDigit() 方法来完成。我们在讲**[基本数据类型的包装器类型](https://javabetter.cn/basic-grammar/basic-data-type.html)**时曾讲过这个方法,不知道大家还记得不?

唯一需要注意的是,我们需要考虑正负号的情况,所以我们需要在找到正负号后,再往后找数字。

class Solution {

        if (str == null || str.length() == 0) {            return 0;        }
        str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
        int sign = 1;        int index = 0;
        // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
        // 提取数字        StringBuilder numberStr = new StringBuilder();        while (index < str.length() && Character.isDigit(str.charAt(index))) {            numberStr.append(str.charAt(index));            index++;        }
        // 没有数字        if (numberStr.length() == 0) {            return 0;        }
        // 尝试转换        try {            return Integer.parseInt(numberStr.toString()) * sign;        } catch (NumberFormatException e) {            return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;        }    }}

代码非常简单,先去掉空格,再判断正负,然后遍历字符串,找出数字的部分,最后尝试通过 Integer.parseInt() 方法来转换,如果转换失败,就返回 Integer.MIN_VALUE 或者 Integer.MAX_VALUE。

比较遗憾的是,这样的代码效率不高。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_09.png)

### **分析 2**

效率低的原因在于,我们在找数字的时候,已经通过 Character.isDigit() 方法判断了字符是否为数字,但是我们还是把它存到了 StringBuilder 中,最后再通过 Integer.parseInt() 方法来转换,这样做的话,就有点多此一举了。

我们可以直接在遍历的过程中,就把数字转换出来,这样就不需要再通过 Integer.parseInt() 方法来转换了。

class Solution {

        if (str == null || str.length() == 0) {            return 0;        }
        str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
        int sign = 1;        int index = 0;
        // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
        // 提取数字        int result = 0;        while (index < str.length() && Character.isDigit(str.charAt(index))) {            int digit = str.charAt(index) - '0';            if (result > (Integer.MAX_VALUE - digit) / 10) {                return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;            }            result = result * 10 + digit;            index++;        }
        return result * sign;    }}

while 循环前面都很好懂,我来详细解释一下 while 循环中的代码:

while (index int digit = str.charAt(index) - '0'; // 溢出检查:如果当前的 result 大于最大整数减去当前数字除以 10, // 则表示下一步操作(result * 10 + digit)会导致溢出 if (result > (Integer.MAX_VALUE - digit) / 10) { // 根据符号返回最大或最小整数 return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; } // 更新结果:将当前数字累加到结果中 result = result * 10 + digit; // 移动到字符串的下一个字符 index++;}

  • 循环条件:index < str.length () && Character.isDigit(str.charAt(index)) 确保只有在字符串没有结束且当前字符是数字时,循环才继续。
  • 字符转数字:int digit = str.charAt(index) - '0'; 将字符型的数字转换为整型。在 ASCII 码表中,数字字符 '0' 到 '9' 是连续排列的,所以 str.charAt(index) - '0' 能够得到相应的整数值。
  • 溢出检查:if (result > (Integer.MAX_VALUE - digit) / 10) 检查是否即将发生溢出。因为下一步操作是 result * 10 + digit,如果 result 已经大于 (Integer.MAX_VALUE - digit) / 10,则乘以 10 后加上 digit 一定会超出 int 的范围。这里使用 (Integer.MAX_VALUE - digit) / 10 作为阈值来避免在乘法之后才检测溢出,那时可能已经晚了。
  • 更新结果:result = result * 10 + digit; 将当前数字字符的值累加到 result 中。这里的 result * 10 是为了将之前的结果左移一位(即乘以 10),然后加上当前的数字。

这个 while 循环就很精巧,其实就和 parseInt 方法的处理很接近了。

OK,我们再来看一下运行效率。

果然提高了一大截。

总结

如果你之前认真学过 Java 基础的话,比如说去空格,比如说判断字符的正负号,比如说判断字符是否为数字,比如说把一个字符转成数字。

这些知识点我在《二哥的 Java 进阶之路》上都有给大家讲过,比如说:

力扣链接:https://leetcode.cn/problems/string-to-integer-atoi/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

该题解目前只针对**二哥编程星球 **的球友开放,如果你也想一起打卡学习算法的话,扫下面的优惠券加入我们吧,星球目前 4100+ 人,定价 149 元,从当初的 99 元一路涨上来的,所以早就是优势,别再犹豫了。

(优惠券仅 300 张,数量有限)

一个人可以走得很快,但一群人才能走得更远。如果想去面中大厂的话,LeetCode 几乎是必刷的,二哥的 LeetCode 题解通俗易懂、图文并茂,理解给你举一反三的解题能力,冲鸭。

    - 语雀专栏地址+密码可通过星球置顶帖查看:[https://t.zsxq.com/15rEo9Pdu](https://t.zsxq.com/15rEo9Pdu)
  • 技术派专栏地址:https://paicoding.com/column/7/8

  • 星球原地址:https://t.zsxq.com/15u5YkmHY

  • 这道题可以说完全重写了,给出两道大家完全都能掌握的题解,并且追加手绘图,大家会非常容易掌握,一道中等难度直接变身 easy 难度。

  • 增加代码注释,这样大家更容易看懂并掌握题解;

  • 增加参考资料,这样大家可以从零基础直接开刷,减轻新手刷题的阻力;

  • 增加手绘图,这样大家更容易理解解题思路;

  • 增加 ACM 输入输出,这样大家可以在 Intellij IDEA 中进行调试;

  • 增加题解模拟,这样大家可以根据题解模拟整个题解过程,看完会有一种“哦哦”恍然大悟的感觉;

  • 图片上传至 OSS 并配合 CDN,提升大家在技术派网站中的阅读体感。

  • 另外,这一版本会作为 PDF 的终稿随后在星球进行发布。

题意

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  • 读入字符串并丢弃无用的前导空格
  • 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  • 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  • 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  • 如果整数数超过 32 位有符号整数范围 [, − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 的整数应该被固定为 ,大于 − 1 的整数应该被固定为 − 1 。
  • 返回最终结果。

示例

输入:s = "42"

第 1 步:"42"(当前没有读入字符,因为没有前导空格) ^第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^第 3 步:"42"(读入 "42",到达末尾) ^解析得到整数 42 。由于 "42" 在 32 位有符号整数范围内,最终结果为 42 。

输入:s = "   -42"

第 1 步:" -42"(读入前导空格,但忽视掉) ^第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数) ^第 3 步:" -42"(读入 "42",到达末尾) ^解析得到整数 -42 。由于 "-42" 在 32 位有符号整数范围内,最终结果为 -42 。

输入:s = "4193 with words"

第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格) ^第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止) ^解析得到整数 4193 。由于 "4193" 在 32 位有符号整数范围内,最终结果为 4193 。

难度

分析 1

其实这道题的描述对于 Java 党来说,实在是不够友好,在 Java 中,我们都喜欢叫“方法”而不是函数,更没有 atoi 函数。

不过,读完这道题目,很容易联想到 Java 当中的 Integer.parseInt() 方法,这个方法就是来完成数字字符串转整数的,对吧?

我之前在《二哥的 Java 进阶之路 》上也带大家分析过该方法的源码,不过 parseInt 方法仅处理了正负的情况,并没有处理空格的情况、字符串中包含字符的情况,不过我们可以在此基础上进行扩展,来完成这道题目。

①、处理空格我们就用 String 的 trim() 方法来完成。

②、接着我们需要把字符串中数字部分找出来,这里我们可以通过 Character.isDigit() 方法来完成。我们在讲**基本数据类型的包装器类型 **时曾讲过这个方法,不知道大家还记得不?

唯一需要注意的是,我们需要考虑正负号的情况,所以我们需要在找到正负号后,再往后找数字。

class Solution {
    if (str == null || str.length() == 0) {            return 0;        }
    str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
    int sign = 1;        int index = 0;
    // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
    // 提取数字        StringBuilder numberStr = new StringBuilder();        while (index < str.length() && Character.isDigit(str.charAt(index))) {            numberStr.append(str.charAt(index));            index++;        }
    // 没有数字        if (numberStr.length() == 0) {            return 0;        }
    // 尝试转换        try {            return Integer.parseInt(numberStr.toString()) * sign;        } catch (NumberFormatException e) {            return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;        }    }}

代码非常简单,先去掉空格,再判断正负,然后遍历字符串,找出数字的部分,最后尝试通过 Integer.parseInt() 方法来转换,如果转换失败,就返回 Integer.MIN_VALUE 或者 Integer.MAX_VALUE。

比较遗憾的是,这样的代码效率不高。

分析 2

效率低的原因在于,我们在找数字的时候,已经通过 Character.isDigit() 方法判断了字符是否为数字,但是我们还是把它存到了 StringBuilder 中,最后再通过 Integer.parseInt() 方法来转换,这样做的话,就有点多此一举了。

我们可以直接在遍历的过程中,就把数字转换出来,这样就不需要再通过 Integer.parseInt() 方法来转换了。

class Solution {
    if (str == null || str.length() == 0) {            return 0;        }
    str = str.trim(); // 去除前导空格        if (str.length() == 0) return 0;
    int sign = 1;        int index = 0;
    // 检查正负号        if (str.charAt(0) == '+' || str.charAt(0) == '-') {            sign = str.charAt(0) == '-' ? -1 : 1;            index++;        }
    // 提取数字        int result = 0;        while (index < str.length() && Character.isDigit(str.charAt(index))) {            int digit = str.charAt(index) - '0';            if (result > (Integer.MAX_VALUE - digit) / 10) {                return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;            }            result = result * 10 + digit;            index++;        }
    return result * sign;    }}

while 循环前面都很好懂,我来详细解释一下 while 循环中的代码:

while (index     int digit = str.charAt(index) - '0';
    // 溢出检查:如果当前的 result 大于最大整数减去当前数字除以 10,    // 则表示下一步操作(result * 10 + digit)会导致溢出    if (result > (Integer.MAX_VALUE - digit) / 10) {        // 根据符号返回最大或最小整数        return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;    }
    // 更新结果:将当前数字累加到结果中    result = result * 10 + digit;
    // 移动到字符串的下一个字符    index++;}

- **循环条件**:index < [str.length](http://str.length)() && Character.isDigit(str.charAt(index)) 确保只有在字符串没有结束且当前字符是数字时,循环才继续。
- **字符转数字**:int digit = str.charAt(index) - '0'; 将字符型的数字转换为整型。在 ASCII 码表中,数字字符 '0' 到 '9' 是连续排列的,所以 str.charAt(index) - '0' 能够得到相应的整数值。
- **溢出检查**:if (result > (Integer.MAX_VALUE - digit) / 10) 检查是否即将发生溢出。因为下一步操作是 result * 10 + digit,如果 result 已经大于 (Integer.MAX_VALUE - digit) / 10,则乘以 10 后加上 digit 一定会超出 int 的范围。这里使用 (Integer.MAX_VALUE - digit) / 10 作为阈值来避免在乘法之后才检测溢出,那时可能已经晚了。
- **更新结果**:result = result * 10 + digit; 将当前数字字符的值累加到 result 中。这里的 result * 10 是为了将之前的结果左移一位(即乘以 10),然后加上当前的数字。

这个 while 循环就很精巧,其实就和 parseInt 方法的处理很接近了。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_10.png)

OK,我们再来看一下运行效率。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_11.png)

果然提高了一大截。

### **总结**

如果你之前认真学过 Java 基础的话,比如说去空格,比如说判断字符的正负号,比如说判断字符是否为数字,比如说把一个字符转成数字。

这些知识点我在《二哥的 Java 进阶之路》上都有给大家讲过,比如说:

- **[判断字符是否为数字](https://javabetter.cn/basic-grammar/basic-data-type.html)**
- **[把一个字符转成数字](https://javabetter.cn/basic-grammar/basic-data-type.html#_04%E3%80%81int-%E5%92%8C-char-%E7%B1%BB%E5%9E%8B%E4%BA%92%E8%BD%AC)**

力扣链接:**[https://leetcode.cn/problems/string-to-integer-atoi/](https://leetcode.cn/problems/string-to-integer-atoi/)**

### **一步一个脚印**

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

该题解目前只针对**[二哥编程星球](https://javabetter.cn/zhishixingqiu/)**的球友开放,如果你也想一起打卡学习算法的话,扫下面的优惠券加入我们吧,星球目前 4100+ 人,定价 149 元,从当初的 99 元一路涨上来的,所以早就是优势,别再犹豫了。

![图片](https://clxhuhhr.top/uploads/weblog/erge/erge_lc_image_12.png)

(优惠券仅 300 张,数量有限)

一个人可以走得很快,但一群人才能走得更远。如果想去面中大厂的话,LeetCode 几乎是必刷的,二哥的 LeetCode 题解通俗易懂、图文并茂,理解给你举一反三的解题能力,冲鸭。

        
                
                
                知识星球
                
        
        
            
            扫码加入星球
            查看更多优质内容
        
        https://wx.zsxq.com/mweb/views/joingroup/join_group.html?group_id=15522885221412
008.字符串转换整数(atoi)
http://www.clxhxhhr.top/posts/2433/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。