415. 字符串相加

目录

题目链接:

题目:

解题思路:

代码:

总结:


题目链接:

415. 字符串相加 - 力扣(LeetCode)

题目:

  1. 字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何内置的用于处理大整数的库(比如 BigInteger),也不能直接将输入的字符串转换为整数形式。

示例 1:

输入: num1 = "11", num2 = "123"

输出: "134"

示例 2:

输入: num1 = "456", num2 = "77"

输出: "533"

示例 3:

输入: num1 = "0", num2 = "0"

输出: "0"

提示:

解题思路:

使用StringBuffer可变字符串来接受每位的结果,然后使用i,j双指针从 每个字符串的末尾开始相加,使用carry来表示每位结果和下一位的进位

代码:

java 复制代码
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 直接操作数组,效率高,适合频繁拼接。

  1. 为什么从末尾开始遍历?

手工加法是从低位(个位)开始算,字符串的末尾正好对应数字的低位,所以双指针从 length-1 开始,天然契合手工逻辑。

  1. 进位的处理逻辑

carry 变量贯穿整个过程:

初始为 0(还没相加时,进位是 0);

每一位计算后,carry 被更新为「新的进位」;

循环结束后,可能还有剩余进位(比如 999 + 1,最后进位是 1),必须补上。

  1. 字符转数字的细节

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题&quot;字符串相加&quot;的解法。该题要求模拟手工加法,在不直接转换整数的情况下,对两个字符串形式的非负整数进行相加。文章详细拆解了代码实现思路:使用双指针从字符串末尾开始遍历,逐位相加并处理进位,最后反转字符串得到正确结果。重点分析了核心逻辑、边界场景处理、算法复杂度,并对比了其他解法。该方案时间复杂度O(max(M,N)),空间复杂度O(max(M,N)),完美模拟了手工加法过程,具有良好的鲁棒性和可扩展性。

相关推荐
luoqice2 分钟前
linux下找到指定目录下最新日期log文件
linux·算法
楽码20 分钟前
底层技术SwissTable的实现对比
数据结构·后端·算法
fffcccc111228 分钟前
初级背包问题,层层剖析为什么这样做。最好需要自己推演一遍。
算法
瓦特what?1 小时前
关于C++的#include的超超超详细讲解
java·开发语言·数据结构·c++·算法·信息可视化·数据挖掘
楽码2 小时前
自动修复GoVet:语言实现对比
后端·算法·编程语言
lifallen2 小时前
JCTools 无锁并发队列基础:ConcurrentCircularArrayQueue
java·开发语言·数据结构·算法
来自天蝎座的孙孙4 小时前
洛谷P1595讲解(加强版)+错排讲解
python·算法
GawynKing4 小时前
图论(5)最小生成树算法
算法·图论·最小生成树
试剂界的爱马仕4 小时前
胶质母细胞瘤对化疗的敏感性由磷脂酰肌醇3-激酶β选择性调控
人工智能·科技·算法·机器学习·ai写作