LeetCode-415:字符串相加,模拟竖式加法,逐位相加处理进位

本题在线练习LeetCode 415. 字符串相加 — 在线练习(免费 · 无需登录 · AI 辅助)

配套刷题网站 Zero2Leetcode —— 内置本地 OJ + AI 教练,零门槛开刷 Hot 100。

题目概述

给定两个以字符串形式表示的非负整数 num1num2,返回它们的和(同样以字符串形式)。

限制:不能使用 int() 将整个字符串直接转成整数,也不能使用任何 BigInteger 库。

例如:

  • 输入:num1 = "11", num2 = "123" → 输出:"134"
  • 输入:num1 = "456", num2 = "77" → 输出:"533"
  • 输入:num1 = "0", num2 = "0" → 输出:"0"

这道题是字节面试高频题,面试官通常会明确要求不能用 int() 做整体转换,考察的就是你能不能手写出逐位加法的逻辑。

核心思路:模拟小学竖式加法

回忆一下我们在纸上做加法的过程:

  1. 从个位(最右边)开始,逐位往左加
  2. 每一位把两个数字和上一轮的进位加起来
  3. 如果和 >= 10,就产生进位 1,当前位只保留个位数字
  4. 两个数长度不同时,短的那个前面补零继续加
  5. 最后如果还有进位,在最前面补一个 1

翻译成代码就是:

  • 用两个指针 ij 分别从 num1num2 的末尾开始,同时往左移动
  • 每一轮取出当前位的数字(用 ord(c) - ord('0') 把字符转成数字,避免使用 int()),加上进位
  • 把当前位的结果追加到一个列表里
  • 全部加完后,把列表反转拼接成字符串

为什么要反转?因为我们是从低位(右边)开始算的,每一轮的结果是从个位到高位的顺序,和最终字符串的顺序刚好相反。

代码

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        i = len(num1) - 1
        j = len(num2) - 1
        carry = 0
        result = []

        while i >= 0 or j >= 0 or carry:
            x = ord(num1[i]) - ord('0') if i >= 0 else 0
            y = ord(num2[j]) - ord('0') if j >= 0 else 0

            total = x + y + carry
            carry = total // 10
            result.append(str(total % 10))

            i -= 1
            j -= 1

        return ''.join(reversed(result))

逐行拆解

1. 初始化双指针和进位

i = len(num1) - 1
j = len(num2) - 1
carry = 0
result = []

ij 分别指向 num1num2 的最后一个字符(个位)。carry 记录进位,初始为 0。result 是一个列表,用来收集每一位的计算结果。


2. 循环条件:还有位没加完,或者还有进位

while i >= 0 or j >= 0 or carry:

三种情况需要继续:

  • num1 还有位没处理(i >= 0
  • num2 还有位没处理(j >= 0
  • 上一轮产生了进位(比如 "999" + "1",最高位加完后 carry 还是 1)

carry 放进条件,就不用在循环外单独判断是否还有进位了。


3. 取当前位的数字值

x = ord(num1[i]) - ord('0') if i >= 0 else 0
y = ord(num2[j]) - ord('0') if j >= 0 else 0

ord('5') - ord('0') 的结果是 5,这是把单个字符转成对应数字的标准技巧,不需要调用 int()

如果某个字符串已经遍历完(指针 < 0),对应位的值就是 0,相当于竖式加法中短数前面补零。


4. 计算当前位的结果和新进位

total = x + y + carry
carry = total // 10
result.append(str(total % 10))
  • total 是当前位两个数字加进位的总和(范围是 0~19)
  • carry = total // 10:整除 10 得到新进位(0 或 1)
  • total % 10:取个位数字,转成字符追加到结果列表

5. 移动指针

i -= 1
j -= 1

两个指针同时左移。即使某个指针已经 < 0,继续减也没关系,因为下一轮取值时会走 else 0 的分支。


6. 反转拼接返回结果

return ''.join(reversed(result))

result 中存的顺序是从个位到高位,例如 "456" + "77" 会产生 ['3', '3', '5']。反转后拼接成 "533" 就是最终答案。

手动模拟

num1 = "456", num2 = "77" 为例(即 456 + 77 = 533):

步骤 i j num1[i] num2[j] carry(进入时) total 当前位 carry(产出)
第 1 轮 2 1 '6' '7' 0 13 3 1
第 2 轮 1 0 '5' '7' 1 13 3 1
第 3 轮 0 -1 '4' 补 0 1 5 5 0

循环结束:i = -1j = -2carry = 0,三个条件都不满足。

result = ['3', '3', '5'],反转后 → "533"


再看一个需要最终进位的例子:num1 = "999", num2 = "1"(即 999 + 1 = 1000):

步骤 i j num1[i] num2[j] carry(进入时) total 当前位 carry(产出)
第 1 轮 2 0 '9' '1' 0 10 0 1
第 2 轮 1 -1 '9' 补 0 1 10 0 1
第 3 轮 0 -2 '9' 补 0 1 10 0 1
第 4 轮 -1 -3 补 0 补 0 1 1 1 0

第 4 轮时两个字符串都遍历完了,但 carry = 1,所以循环继续,生成了最高位的 1

result = ['0', '0', '0', '1'],反转后 → "1000"

复杂度分析

时间复杂度:O(max(m, n))

  • mn 分别是 num1num2 的长度
  • 循环次数等于较长字符串的长度(如果最后有进位,再多一轮)
  • 每一轮只做常数次操作

空间复杂度:O(max(m, n))

  • result 列表的长度最多为 max(m, n) + 1
  • 除此之外只用了几个额外变量

总结

这道题的核心就是模拟竖式加法,和 LeetCode 2(两数相加)的思路几乎一样,区别在于这里操作的是字符串而不是链表。

几个关键点:

  • ord(c) - ord('0') 代替 int(c):面试中常被要求不能用 int() 做任何转换,这个写法是标准替代方案
  • 循环条件写 while i >= 0 or j >= 0 or carry:把进位纳入循环条件,不需要循环外再单独判断
  • 结果列表最后反转:从低位到高位计算,最后 reversed 得到正确顺序
  • 短字符串补零:指针越界时值当作 0,统一处理不等长的情况

这是一道字节面试高频题,看起来简单但细节不少。把"双指针从右到左 + 进位 + 反转"这套模板练熟,面试时就能快速写对。

posted @ 2026-04-20 00:21  孤飞  阅读(63)  评论(0)    收藏  举报