剑指offer-49、把字符串转换成整数

题⽬描述

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

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

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

注意:

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

示例1: 输⼊:s = "42" 输出:42 解释:加粗的字符串为已经读⼊的字符,插⼊符号是当前读取的字符。

  1. 第 1 步:"42"(当前没有读⼊字符,因为没有前导空格)
  2. 第 2 步:"42"(当前没有读⼊字符,因为这⾥不存在 '-' 或者 '+')
  3. 第 3 步:"42"(读⼊ "42")
  4. 解析得到整数 42 。
  5. 由于 "42" 在范围 -231, 231 - 1 内,最终结果为 42 。

示例2: 输⼊:s = " -42" 输出:-42 解释:

  1. 第 1 步:" -42"(读⼊前导空格,但忽视掉)
  2. 第 2 步:" -42"(读⼊ '-' 字符,所以结果应该是负数)
  3. 第 3 步:" -42"(读⼊ "42")
  4. 解析得到整数 -42 。
  5. 由于 "-42" 在范围 -231, 231 - 1 内,最终结果为 -42 。

示例3: 输⼊:s = "4193 with words" 输出:4193 解释:

  1. 第 1 步:"4193 with words"(当前没有读⼊字符,因为没有前导空格)
  2. 第 2 步:"4193 with words"(当前没有读⼊字符,因为这⾥不存在 '-' 或者 '+')
  3. 第 3 步:"4193 with words"(读⼊ "4193";由于下⼀个字符不是⼀个数字,所以读⼊停⽌)
  4. 解析得到整数 4193 。
  5. 由于 "4193" 在范围 -231, 231 - 1 内,最终结果为 4193 。

示例4: 输⼊:s = "words and 987" 输出:0 解释:

  1. 第 1 步:"words and 987"(当前没有读⼊字符,因为没有前导空格)
  2. 第 2 步:"words and 987"(当前没有读⼊字符,因为这⾥不存在 '-' 或者 '+')
  3. 第 3 步:"words and 987"(由于当前字符 'w' 不是⼀个数字,所以读⼊停⽌)
  4. 解析得到整数 0 ,因为没有读⼊任何数字。
  5. 由于 0 在范围 -231, 231 - 1 内,最终结果为 0 。

示例5: 输⼊:s = "-91283472332" 输出:-2147483648 解释:

  1. 第 1 步:"-91283472332"(当前没有读⼊字符,因为没有前导空格)
  2. 第 2 步:"-91283472332"(读⼊ '-' 字符,所以结果应该是负数)
  3. 第 3 步:"-91283472332"(读⼊ "91283472332")
  4. 解析得到整数 -91283472332 。
  5. 由于 -91283472332 ⼩于范围 -231, 231 - 1 的下界,最终结果被截断为 -231 = -2147483648

提示:

  • 0 <= s.length <= 200
  • s 由英⽂字⺟(⼤写和⼩写)、数字(0-9)、' '、'+'、'-' 和 '.' 组成

思路与解答

基础遍历法

这道题⽬看起来很⻓,但是实际上逻辑很清晰,就是将字符串解析成为数字,⾥⾯有⼏个特殊的则:

  1. 前⾯的空格去掉,不读取
  2. 接下来的字符必须是数字," + "号或者" - "号
    1. 如果是" + "号或者" - "号,将符号记录下来
    2. 没有符号默认是" + "号,正数。
  3. 接下来的字符必须是数字,遇到其他字符会直接结束
  4. 需要考虑溢出的问题

在将字符串转换成数字的时候,⽤下⾯这句核⼼代码:

java 复制代码
sum = sum * 10 + (str.charAt(i) - '0');

但是在这个过程中,我们依然需要考虑数字溢出的问题,针对这种情况,我们可以在加和之前判断,针对⼤于0的情况,如果⼤于最⼤值整除10,或者等于最⼤值整除10,但是个位数超过了,都直接返回0。

假设最⼤值是127,那么sum如果⼤于12,肯定会超过,如果sum == 12,但是个位数⼤于7,乘以10相加,也肯定会超。

java 复制代码
if (sum > Integer.MAX_VALUE/10 || (sum == Integer.MAX_VALUE / 10 &&
(str.charAt(i) - '0') > 7)) return 0;

对于⼩于0 的情况,假设最⼩值是-128 ,那么sum 是数字部分 128 , 如果当前sum ⼤于 12 ,那么就⼀定超出,或者sum == 12 ,但是个位数⼤于8 ,乘以10 ,相加也会⼤于128 ,不符合要求,所以直接返回0 。

java 复制代码
if (sum < Integer.MIN_VALUE/10 || (sum == Integer.MIN_VALUE / 10 && x
(str.charAt(i) - '0') > 8)) return 0;

具体代码实现:

java 复制代码
public class Solution {
    public static int myAtoi(String str) {
        if (str == null) {
            return 0;
        }
        int i = 0;
        while (i < str.length() && str.charAt(i) == ' ') {
            i++;
        }
        if (i >= str.length() || (str.charAt(i) != '-' && str.charAt(i) != '+' && !((str.charAt(i) >= '0') &&
                (str.charAt(i) <= '9')))) {
            return 0;
        }
        int sign = 1;
        if (i < str.length() && (str.charAt(i) == '-' || i < str.length() &&
                str.charAt(i) == '+')) {
            sign = str.charAt(i) == '-' ? -1 : 1;
            i++;
        }
        int sum = 0;
        for (; i < str.length(); i++) {
            if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
                if (sign == 1) {
                    if (sum > Integer.MAX_VALUE / 10 || sum == Integer.MAX_VALUE / 10 && (str.charAt(i) - '0') > 7) {
                        return Integer.MAX_VALUE;
                    }
                } else {
                    if (sum > (Integer.MAX_VALUE) / 10 || sum ==
                        (Integer.MAX_VALUE) / 10 && (str.charAt(i) - '0') > 8) {
                        return Integer.MIN_VALUE;
                    }
                }
                sum = sum * 10 + (str.charAt(i) - '0');
            } else {
                return sum * sign;
            }
        }
        return sum * sign;
    }
}
  • 时间复杂度为 O(n)
  • 空间复杂度为 O(1)。

正则表达式

使用正则表达式来匹配数字模式,代码更加简洁

java 复制代码
public class Solution {
    public int myAtoi(String s) {
        if (s == null) return 0;
        
        s = s.trim();
        if (s.length() == 0) return 0;
        
        // 使用正则表达式匹配数字模式:可选符号位+数字[3](@ref)
        Pattern pattern = Pattern.compile(&#34;^[+-]?\\d+&#34;);
        Matcher matcher = pattern.matcher(s);
        
        if (!matcher.find()) {
            return 0; // 没有匹配到数字模式
        }
        
        String numStr = matcher.group();
        int sign = 1;
        int startIndex = 0;
        
        // 处理符号位
        if (numStr.charAt(0) == '+') {
            startIndex = 1;
        } else if (numStr.charAt(0) == '-') {
            sign = -1;
            startIndex = 1;
        }
        
        long result = 0; // 使用long防止转换过程中溢出
        
        // 转换数字部分
        for (int i = startIndex; i < numStr.length(); i++) {
            int digit = numStr.charAt(i) - '0';
            result = result * 10 + digit;
            
            // 检查是否溢出int范围
            if (sign == 1 && result > Integer.MAX_VALUE) {
                return Integer.MAX_VALUE;
            }
            if (sign == -1 && -result < Integer.MIN_VALUE) {
                return Integer.MIN_VALUE;
            }
        }
        
        return (int) result * sign;
    }
}
  • 时间复杂度:O(n),正则匹配和数字转换都是线性时间
  • 空间复杂度:O(n),需要存储匹配到的子字符串
相关推荐
xcl09253 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐5 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长5 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn5 小时前
Spring 注解使用详解
java·spring
一条小小yu6 小时前
Spring IoC的理解
java·后端·spring
落魄实习生6 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈6 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白8 小时前
核心功能(Service接口)
java·mybatis
乌暮8 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习