LeetCode-415:字符串相加,模拟竖式加法,逐位相加处理进位
本题在线练习:LeetCode 415. 字符串相加 — 在线练习(免费 · 无需登录 · AI 辅助)
配套刷题网站 Zero2Leetcode —— 内置本地 OJ + AI 教练,零门槛开刷 Hot 100。
题目概述
给定两个以字符串形式表示的非负整数 num1 和 num2,返回它们的和(同样以字符串形式)。
限制:不能使用 int() 将整个字符串直接转成整数,也不能使用任何 BigInteger 库。
例如:
- 输入:
num1 = "11",num2 = "123"→ 输出:"134" - 输入:
num1 = "456",num2 = "77"→ 输出:"533" - 输入:
num1 = "0",num2 = "0"→ 输出:"0"
这道题是字节面试高频题,面试官通常会明确要求不能用 int() 做整体转换,考察的就是你能不能手写出逐位加法的逻辑。
核心思路:模拟小学竖式加法
回忆一下我们在纸上做加法的过程:
- 从个位(最右边)开始,逐位往左加
- 每一位把两个数字和上一轮的进位加起来
- 如果和 >= 10,就产生进位 1,当前位只保留个位数字
- 两个数长度不同时,短的那个前面补零继续加
- 最后如果还有进位,在最前面补一个
1
翻译成代码就是:
- 用两个指针
i和j分别从num1和num2的末尾开始,同时往左移动 - 每一轮取出当前位的数字(用
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 = []
i 和 j 分别指向 num1 和 num2 的最后一个字符(个位)。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 = -1,j = -2,carry = 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))
m和n分别是num1和num2的长度- 循环次数等于较长字符串的长度(如果最后有进位,再多一轮)
- 每一轮只做常数次操作
空间复杂度: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,统一处理不等长的情况
这是一道字节面试高频题,看起来简单但细节不少。把"双指针从右到左 + 进位 + 反转"这套模板练熟,面试时就能快速写对。

浙公网安备 33010602011771号