415. 字符串相加
目录
题目链接:
题目:
解题思路:
代码:
总结:
题目链接:
415. 字符串相加 - 力扣(LeetCode)
题目:
415. 字符串相加
给定两个字符串形式的非负整数 num1
和 num2
,计算它们的和并同样以字符串形式返回。
你不能使用任何内置的用于处理大整数的库(比如 BigInteger
),也不能直接将输入的字符串转换为整数形式。
示例 1:
输入: num1 = "11", num2 = "123"
输出: "134"
示例 2:
输入: num1 = "456", num2 = "77"
输出: "533"
示例 3:
输入: num1 = "0", num2 = "0"
输出: "0"
提示:
解题思路:
使用StringBuffer可变字符串来接受每位的结果,然后使用i,j双指针从 每个字符串的末尾开始相加,使用carry来表示每位结果和下一位的进位
代码:
class Solution {public String addStrings(String num1, String num2) {StringBuilder s=new StringBuilder("");int i=num1.length()-1;int j=num2.length()-1;int carry=0;while(i>=0||j>=0){carry+=i>=0?num1.charAt(i)-'0':0;carry+=j>=0?num2.charAt(j)-'0':0;s.append(carry%10);carry/=10;i--;j--;}if(carry==1) s.append(1);return s.reverse().toString();}
}
以下从问题背景、代码结构、逐行逻辑、算法细节、边界场景、优缺点、扩展对比等维度,深入拆解这段字符串相加代码,帮你吃透核心逻辑(
一、问题背景:字符串相加的意义
LeetCode 第 415 题要求模拟手工加法:把两个用字符串表示的非负整数(可能非常大,超出 long 范围),按「竖式加法」规则求和,返回字符串结果。
核心约束:不能直接转整数(比如 Integer.parseInt),必须手动实现进位逻辑 —— 这正是这段代码要解决的问题。
二、整体思路:竖式加法的模拟
手工计算加法时,我们会:
对齐末尾:从个位(字符串末尾)开始逐位相加;
处理进位:每一位的和 = 位 1 + 位 2 + 进位,结果取个位(sum % 10),新的进位取十位(sum / 10);
处理剩余进位:如果最后进位还有剩余(比如 99 + 1 最后进位为 1),要补到结果里。
这段代码完全模拟了这个过程,用指针、进位变量、字符串拼接实现。
三、代码逐行拆解(核心逻辑)
类与方法定义
java
运行
class Solution {
public String addStrings(String num1, String num2) {
Solution 是 LeetCode 约定的解题类,addStrings 是题目要求的方法,入参是两个字符串 num1、num2,返回值是相加后的字符串。
初始化变量
java
运行
StringBuilder s = new StringBuilder("");
int i = num1.length() - 1;
int j = num2.length() - 1;
int carry = 0;
StringBuilder s:动态拼接结果。因为要频繁在末尾追加字符(类似手工从个位开始写结果),最后再反转,比 String 高效。
i、j:双指针,分别指向 num1、num2 的末尾字符(模拟手工从个位开始遍历)。
carry:进位值(初始为 0),记录每一位相加后的进位(比如 8+5=13,进位 1)。
核心循环:逐位相加
java
运行
while (i >= 0 || j >= 0) {
carry += i >= 0 ? num1.charAt(i) - '0' : 0;
carry += j >= 0 ? num2.charAt(j) - '0' : 0;
s.append(carry % 10);
carry /= 10;
i--;
j--;
}
这是代码的灵魂,逐行拆解:
处理指针越界:
i >= 0 ? num1.charAt(i) - '0' : 0:如果 i 没越界(还能取到 num1 的字符),就取该位的数字(char 转 int,通过 - '0' 实现);否则补 0(比如 num1 比 num2 短,高位补 0 对齐)。
同理处理 num2 的 j 指针。
累加进位与当前位:
carry 先加上 num1 当前位,再加上 num2 当前位 ——模拟手工加法的「当前位总和 = 位 1 + 位 2 + 进位」。
计算当前位结果,更新进位:
s.append(carry % 10):当前位总和的个位是结果位(比如 13 % 10 = 3,记为当前位结果)。
carry /= 10:当前位总和的十位是新的进位(比如 13 / 10 = 1,进位更新为 1)。
指针前移:
i--; j--;:双指针同时左移,处理下一位(更高位)。
处理最后进位
java
运行
if (carry == 1) s.append(1);
循环结束后,如果还有进位(比如 99 + 1,最后进位是 1),需要补到结果最高位。
反转结果
java
运行
return s.reverse().toString();
因为我们是从个位开始拼接结果(比如先拼 3,再拼 2,得到 "32"),所以需要反转成正常顺序("23")。
四、算法细节:为什么这么写?
1. 为什么用 StringBuilder?
String 是不可变对象,每次 + 都会创建新对象,效率低;
StringBuilder 是可变的,append 直接操作数组,效率高,适合频繁拼接。
2. 为什么从末尾开始遍历?
手工加法是从低位(个位)开始算,字符串的末尾正好对应数字的低位,所以双指针从 length-1 开始,天然契合手工逻辑。
3. 进位的处理逻辑
carry 变量贯穿整个过程:
初始为 0(还没相加时,进位是 0);
每一位计算后,carry 被更新为「新的进位」;
循环结束后,可能还有剩余进位(比如 999 + 1,最后进位是 1),必须补上。
4. 字符转数字的细节
num1.charAt(i) - '0':
char 本质是 Unicode 编码,'0' 的 ASCII 是 48,'1' 是 49…… 所以用 char - '0' 可以把字符转成对应的数字(比如 '5' - '0' = 5)。
五、边界场景测试
场景 1:普通情况(位数相同)
输入:num1 = "123", num2 = "456"
过程:
个位:3+6=9 → 结果 9,进位 0
十位:2+5=7 → 结果 7,进位 0
百位:1+4=5 → 结果 5,进位 0
反转前:"975" → 反转后:"573"(正确)
场景 2:位数不同
输入:num1 = "99", num2 = "1"
过程:
个位:9+1=10 → 结果 0,进位 1
十位:9+0+1=10 → 结果 0,进位 1
循环结束,进位 1 → 补 1
反转前:"001" → 反转后:"100"(正确)
场景 3:全零
输入:num1 = "0", num2 = "0"
过程:
个位:0+0=0 → 结果 0,进位 0
循环结束,进位 0 → 无补充
反转前:"0" → 反转后:"0"(正确)
场景 4:最大进位
输入:num1 = "9999", num2 = "9999"
过程:
每一位相加都会产生进位,最后进位 1 补上,结果 19998(代码可正确处理)。
六、代码优缺点分析
优点:
高效:
时间复杂度 O(max(M, N))(M、N 是两个字符串长度),只遍历一次。
空间复杂度 O(max(M, N))(StringBuilder 存储结果,反转后不额外占空间)。
逻辑清晰:
完美模拟手工加法,双指针、进位、拼接、反转的流程,和人类思维一致,易理解。
鲁棒性:
处理了「位数不同」「最后进位」等所有边界,通过测试。
缺点(几乎没有,硬找的话 ):
依赖 StringBuilder 的 reverse,如果对反转有性能洁癖(实际影响极小),可以用数组手动控制顺序 —— 但没必要,reverse 内部是双指针交换,效率很高。
七、与其他解法对比
解法 2:补零后遍历(低效版)
java
运行
// 伪代码,不推荐
while (num1 长度 < num2 长度) num1 = "0" + num1;
while (num2 长度 < num1 长度) num2 = "0" + num2;
// 然后从 0 开始遍历
缺点:补零会产生额外字符串,空间复杂度更高(尤其数字极大时),不如双指针灵活。
解法 3:递归(不实用)
递归实现手工加法,会因栈深度(位数)过大导致栈溢出,且代码复杂,不如迭代直观。
八、扩展:如何支持负数?
如果题目扩展为「字符串表示的整数(可能负)」,需要额外处理:
提取符号(+/-);
处理绝对值相加或相减(借位逻辑);
符号与结果的拼接。
这段代码的「无符号加法」是基础,扩展时可在此逻辑上封装。
九、总结:代码的价值
这段代码把人类手工加法的逻辑,完美映射到代码中:
双指针模拟「从低位到高位」;
carry 模拟「进位传递」;
StringBuilder 模拟「草稿纸写结果」;
反转操作修复「低位先写」的顺序问题。
它不仅解决了 LeetCode 问题,更传递了 **「算法是人类思维的代码化」** 这一核心思想 —— 理解手工逻辑,再用循环、指针、进位等编程工具实现,就是算法的本质。
如果你在面试中写出这段代码,面试官不仅能看到你对问题的理解,更能看到你把现实逻辑转化为代码的能力 —— 这才是算法题考察的核心。
总结:
本文深入解析了LeetCode 415题"字符串相加"的解法。该题要求模拟手工加法,在不直接转换整数的情况下,对两个字符串形式的非负整数进行相加。文章详细拆解了代码实现思路:使用双指针从字符串末尾开始遍历,逐位相加并处理进位,最后反转字符串得到正确结果。重点分析了核心逻辑、边界场景处理、算法复杂度,并对比了其他解法。该方案时间复杂度O(max(M,N)),空间复杂度O(max(M,N)),完美模拟了手工加法过程,具有良好的鲁棒性和可扩展性。