【LeetCode】字符串转换整数 (atoi) [M](模拟)

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

一、题目

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

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

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

注意:

  • 本题中的空白字符只包括空格字符 ' ' 。
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

示例 1:

输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"42"(读入 "42")
           ^
解析得到整数 42 。
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。

示例 2:

输入:s = "   -42"
输出:-42
解释:
第 1 步:"   -42"(读入前导空格,但忽视掉)
            ^
第 2 步:"   -42"(读入 '-' 字符,所以结果应该是负数)
             ^
第 3 步:"   -42"(读入 "42")
               ^
解析得到整数 -42 。
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。

示例 3:

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

提示:

  • 0 <= s.length <= 200
  • s 由英文字母(大写和小写)、数字(0-9)、' ''+''-' 和 '.' 组成

二、代码

class Solution {
    public int myAtoi(String str) {
        // 字符串转换为字符数组
        char[] s = str.toCharArray();
        // 字符串长度
        int n = s.length;
        // 标记是否已经完成了去除前缀空格的流程
        boolean flag1 = false;
        // 标记是否已经完成了读取符号位的流程
        boolean flag2 = false;
        // 记录符号位
        int signed = 1;
        // 记录答案
        // 这种字符串转数字类型的题,需要在过程中用负数统计,因为系统最小值绝对值比最大值大一个(负数的范围比正数的范围多1个),拿它做底更安全。
        // 比如转换字符串"-2147483648"为一个int数,用正数处理就不够了,只能用负数处理才行。
        // 如果不是负数的,也先将其转化为负数,处理完了之后再转回正数。
        int ans = 0;

        // 下面就是计算两个不能越界的范围,因为在统计过程中我们使用的是负数,所以这里就用负数的最小值来判断溢出边界
        int minq = Integer.MIN_VALUE / 10;
        int minr = Integer.MIN_VALUE % 10;

        // 开始遍历字符串
        for (int i = 0; i < n; i++) {
            // 如果去除前缀空格的流程,并且当前的字符仍然是空格,则跳过该字符继续下一个
            if (!flag1 && s[i] == ' ') {
            } else {
                // 当第一次找到了一个不是空格的字符时,说明第一个阶段已经结束了,将flag设置为true
                flag1 = true;
                // 第一次进入到该分支,flag2一定是false,我们要先去判断第一个非空格的字符是否是符号位
                if (!flag2) {
                    // 先将flag2设置为true,因为该流程只会执行一次
                    flag2 = true;
                    // 如果符号位是负,则将signed设置为-1
                    if (s[i] == '-') {
                        signed = -1;
                        // 该位置是非数字,直接跳过
                        continue;
                    } else if (s[i] == '+') {
                        // 如果有正号符号位,也需要跳过后续的流程
                        continue;
                    }
                }

                // 当完成了第一个流程和第二个流程后,后面就开始读取数字字符了,当第一次读取到非数字字符时,就结束直接跳出循环,后面的字符串就不管了
                if (s[i] >= '0' && s[i] <= '9') {
                    // 【这里有一个技巧点,上面说了我们要按照负数去做处理,这样更安全,所以这里要用'0' - s[i],当所有流程结束后我们再将符号位转回去即可】
                    int num = '0' - s[i];

                    // 【第二个技巧——防溢出】
                    // 因为我们使用负数来收集的,所以循环中的ans一定是一个负数
                    // 	1) ans结果是负数,如果比系统最小/10还小,那么在后面的乘10操作之后必溢出,这种情况就可以直接返回系统最大或系统最小值
                    //  2) 如果ans == 系统最小值,则乘10之后不会溢出,此时res*10=-2147483640,相当于系统最小值把最后一位变成0,也就是说只要是在个位上加一个小于-8的数(即小于系统最小值个位的那个数minr),就一定低于系统最小值,溢出了。在后面的操作中,ans要加num,所以如果num小于minr,则加上这部分必溢出。这种情况就可以直接返回系统最大或系统最小值
                    if (ans < minq || (ans == minq && num  < minr)) {
                        // 根据符号位来决定返回最大值还是最小值
                        return signed == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
                    }
                    // 收集数字
                    ans = ans * 10 + num;
                } else {
                    // 在收集数字的过程中第一次遍历到非数字字符时就结束循环
                    break;
                }
            }
        }

        // 如果此时ans此时正好是系统最小值,但是符号位是正数,我们知道最大值的绝对值比最小值的绝对值小1,所以此时答案应该是已经超过系统最大值了,算溢出了,直接返回系统最大
        if (signed == 1 && ans == Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        
        // 因为之前我们是按照负数来收集,执行到这里就说明最终的答案没有溢出(大于等于系统最小,小于等于系统最大),所以我们直接将ans取相反数,消除之前按负数收集的影响
        ans = -ans;
        // 返回答案  (符号位 * ans)
        return  (signed * ans);
    }
}

三、解题思路 

这道题不难,就是麻烦,就是需要考虑很多判断条件。并且这道题最关键的一个技巧就是判断大数是否溢出的技巧。

详细的讲解都在代码注释中

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值