优秀的编程知识分享平台

网站首页 > 技术文章 正文

剑指Offer:字符串转整数的实现与边界处理

nanyue 2025-09-21 20:24:49 技术文章 1 ℃

在编程面试中,“字符串转换成整数”是一道经典题目,它不仅考察基础的字符串遍历能力,更侧重对边界条件和异常场景的处理。本文将结合问题分析、思路拆解与多语言实现,带你掌握这道题的核心考点,避免常见陷阱。

一、题目解析:明确需求与约束

1. 题目要求

将一个字符串转换成整数,禁止使用语言自带的字符串转整数库函数(如 C++ 的 atoi、Python 的 int());若输入为非法数值或空字符串,返回 0;若为合法数值(含正负号),返回对应整数。

2. 输入输出示例

输入 输出 说明 "+2147483647" 2147483647 合法正数,返回对应数值 "1a33" 0 含非数字字符,非法输入 "-123" -123 合法负数,返回对应数值 ""(空串) 0 空输入,返回 0

二、核心考点:需要规避的 5 类问题

这道题的关键不在于“转换”本身,而在于异常处理与边界控制。在实现前,必须明确以下 5 个需要处理的场景:

  1. 空值判断:输入字符串为空("")或指针为空(C++ 场景);
  2. 正负号处理:字符串开头可能包含 + 或 -,需标记符号并跳过符号位;
  3. 非法字符过滤:遍历中遇到非数字字符(如 a、!),直接判定为非法,返回 0;
  4. 整数溢出检查:32 位整数的取值范围是 [-2^31, 2^31 - 1](即 -2147483648 到 2147483647),超出范围需返回 0;
  5. 结果区分:需区分“合法的 0”(如输入 "0")和“非法的 0”(如输入 "abc"),避免混淆。

三、实现思路:分步骤拆解逻辑

基于上述考点,我们可以将转换过程拆分为 4 个核心步骤,确保每一步覆盖对应的边界场景:

步骤 1:初始化变量与状态

  • 符号标记:用 minus 布尔值标记是否为负数(默认 false,即正数);
  • 结果存储:用 long long(C++)或 int(Python,因 Python 整数无溢出,但需手动判断范围)存储中间结果,避免转换过程中溢出;
  • 状态标记:用 g_nStatus(C++)或类似变量区分“合法结果”与“非法结果”,避免将 0 误判。

步骤 2:处理开头的符号位

  1. 若字符串首字符为 +:标记符号为正数,指针/索引向后移动一位;
  2. 若字符串首字符为 -:标记符号为负数,指针/索引向后移动一位;
  3. 若首字符为数字:直接进入后续遍历;
  4. 若首字符为其他字符(如 a、 ):直接返回 0。

步骤 3:遍历字符串,逐字符转换

  1. 对每个字符,先判断是否为数字(>= '0' 且 <= '9'):
  2. 若是数字:计算当前结果 = 上一轮结果 × 10 + 符号 × (当前字符 - '0')('0' 的 ASCII 码为 48,通过差值获取数字值);
  3. 若不是数字:立即将结果置为 0,跳出循环(判定为非法输入)。
  4. 每次计算后检查溢出:
  5. 正数场景:若结果 > 2147483647(即 0x7fffffff),置为 0 并跳出;
  6. 负数场景:若结果 < -2147483648(即 (signed int)0x80000000),置为 0 并跳出。

步骤 4:更新状态并返回结果

  • 若遍历正常结束(未遇到非法字符):将状态标记为“合法”;
  • 若遍历提前结束(遇到非法字符或溢出):状态保持“非法”;
  • 最终返回转换后的整数(若为 long long 类型,需强制转换为 int)。

四、多语言实现:代码与解析

1. C++ 实现(兼顾溢出与状态标记)

#include <string>
using namespace std;

class Solution {
public:
    // 状态枚举:区分合法结果(kValid)与非法结果(kInValid)
    enum Status { kValid = 0, kInValid };
    // 全局状态变量,外部可通过此变量判断结果是否合法
    int g_nStatus = kValid;

    int StrToInt(string str) {
        g_nStatus = kInValid;  // 初始默认非法,避免合法0与非法0混淆
        long long num = 0;     // 用long long存储中间结果,防止溢出
        const char* cstr = str.c_str();  // 转为C风格字符串,便于指针操作

        // 第一步:判断指针非空且字符串非空
        if (cstr != nullptr && *cstr != '\0') {
            bool minus = false;  // 符号标记:false为正,true为负

            // 第二步:处理正负号
            if (*cstr == '+') {
                cstr++;  // 跳过'+',指针后移
            } else if (*cstr == '-') {
                minus = true;  // 标记为负数
                cstr++;        // 跳过'-',指针后移
            }

            // 第三步:若符号后仍有字符,进入核心转换
            if (*cstr != '\0') {
                num = StrToIntCore(cstr, minus);
            }
        }

        return static_cast<int>(num);  // 强制转为int返回
    }

private:
    // 核心转换函数:处理数字部分,判断溢出
    long long StrToIntCore(const char* cstr, bool minus) {
        long long num = 0;
        while (*cstr != '\0') {
            // 检查当前字符是否为数字
            if (*cstr >= '0' && *cstr <= '9') {
                int flag = minus ? -1 : 1;  // 根据符号确定系数
                // 计算当前数值:上一轮结果×10 + 当前数字×符号
                num = num * 10 + flag * (*cstr - '0');

                // 第四步:检查32位整数溢出
                // 正数溢出:num > 2^31 - 1(0x7fffffff)
                // 负数溢出:num < -2^31((signed int)0x80000000)
                if ((!minus && num > 0x7fffffff) || 
                    (minus && num < static_cast<signed int>(0x80000000))) {
                    num = 0;  // 溢出则置为0
                    break;
                }

                cstr++;  // 指针后移,处理下一个字符
            } else {
                // 遇到非数字字符,置为0并跳出
                num = 0;
                break;
            }
        }

        // 若遍历正常结束(指针指向'\0'),标记为合法状态
        if (*cstr == '\0') {
            g_nStatus = kValid;
        }

        return num;
    }
};

C++ 代码关键说明:

  • **状态变量 g_nStatus**:解决“合法 0”与“非法 0”的区分问题,例如输入 "0" 时 g_nStatus = kValid,输入 "abc" 时 g_nStatus = kInValid;
  • long long 类型:由于 32 位 int 的最大值为 2147483647,若用 int 存储中间结果,计算 214748364 * 10 + 7 时可能溢出,因此用 long long 暂存;
  • 溢出判断:通过十六进制常量 0x7fffffff(2147483647)和 0x80000000(-2147483648)直接判断,避免手动计算幂次出错。

2. Python 实现(简化逻辑,适配语言特性)

Python 的整数无固定长度,理论上不会溢出,但需手动判断是否超出 32 位整数范围;同时 Python 字符串处理更简洁,无需指针操作:

# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, s):
        s = s.strip()  # 可选:去除字符串前后空格(如输入"  -123")
        length = len(s)
        if length == 0:
            return 0  # 空字符串,返回0
        
        minus = False  # 符号标记:False为正,True为负
        start_idx = 0  # 数字部分的起始索引
        
        # 处理正负号
        if s[0] == '+':
            start_idx = 1  # 跳过'+',从索引1开始遍历
        elif s[0] == '-':
            minus = True   # 标记为负数
            start_idx = 1  # 跳过'-',从索引1开始遍历
        
        num = 0
        sign = -1 if minus else 1  # 符号系数:-1为负,1为正
        
        # 遍历数字部分
        for char in s[start_idx:]:
            # 检查是否为数字
            if '0' <= char <= '9':
                # 计算当前数值:上一轮结果×10 + 当前数字×符号
                num = num * 10 + sign * (ord(char) - ord('0'))
                
                # 检查32位整数溢出(超出范围则返回0)
                if num > 2**31 - 1 or num < -2**31:
                    return 0
            else:
                # 遇到非数字字符,返回0
                return 0
        
        return num

Python 代码关键说明:

  • strip() 处理:可选步骤,若题目允许输入包含前后空格(如 " +2147"),可先去除空格,增强鲁棒性;
  • ord() 函数:通过 ord(char) - ord('0') 将字符数字转为整数(如 ord('5') - ord('0') = 5);
  • 溢出判断:直接用 2**31 - 1(2147483647)和 -2**31(-2147483648)判断,符合 Python 语法习惯。

五、常见错误与优化建议

1. 容易踩坑的 3 个点

  • 忽略空字符串:未判断输入为 "" 直接遍历,导致索引越界;
  • 溢出未处理:用 int 存储中间结果,导致转换大数字时溢出(如 C++ 中 214748364 * 10 + 8 会超出 int 范围);
  • 符号位后无数字:输入 "+" 或 "-" 时,未判断后续是否有数字,直接返回 0(需在遍历后确认是否有有效数字)。

2. 优化方向

  • 支持更多场景:若题目允许输入包含前导零(如 "00123"),当前代码已兼容,无需额外处理;
  • 异常抛出:若需要更明确的错误提示,可将“非法输入”改为抛出异常(如 ValueError),而非仅返回 0;
  • 性能优化:C++ 中可直接用 string 遍历(无需转为 C 风格字符串),减少指针操作的复杂度。

六、总结

“字符串转整数”看似简单,实则是对“代码鲁棒性”的全面考察。核心思路是“先处理边界,再逐字符转换,全程检查异常”

  1. 边界处理:空值、符号位、非法字符;
  2. 转换逻辑:利用“结果 × 10 + 当前数字”实现逐位累加;
  3. 溢出控制:根据 32 位整数范围实时判断,避免结果超出限制。

掌握这道题的解题思路,不仅能应对面试,更能培养“考虑所有异常场景”的编程思维,为复杂问题的解决打下基础。

最近发表
标签列表