【LeetCode 刷题系列|第 3 篇】详解大数相加:从模拟竖式到简洁写法的优化之路🔢

🔢 前言

Hello~大家好,我是秋天的一阵风

今天要攻克的是 LeetCode 上的经典 大数计算 题 ------「字符串相加」(题号 415)。

这道题的核心场景是「大数相加」:输入的两个非负整数以字符串形式存储(长度最长可达 5100 位),根本无法直接转成 Number 或 BigInt 类型计算,本质是考察手动模拟大数竖式加法的能力。

它和前两篇的「盛最多水」「接雨水」不同,重点不是算法复杂度优化,而是处理「进位、长度对齐、末尾残留进位」这些大数计算的关键细节,非常适合夯实字符串操作和边界处理思维。

话不多说,咱们一步步拆解,让你彻底掌握大数相加的核心逻辑~

一、LeetCode 大数相加(字符串版)题目详情

1. 题目描述

给定两个非负整数 num1num2,它们以字符串形式表示(即大数),返回它们的和也以字符串形式表示。说明

  • 你不能使用任何内置的 BigInteger 库或直接将输入转换为整数形式(核心限制,凸显大数场景);
  • num1num2 的长度都小于 5100(明确大数规模);
  • num1num2 都只包含数字 0-9
  • num1num2 都不包含前导零(除了数字 0 本身)。

题目链接415. 字符串相加 - 力扣(LeetCode)

2. 示例演示

  • 输入:num1 = "11", num2 = "123"
  • 输出:"134"
  • 解释:11 + 123 = 134,模拟竖式相加:个位 1+3=4,十位 1+2=3,百位 0+1=1,拼接结果为 "134"(小型大数场景,理解基础逻辑)。
  • 输入:num1 = "456", num2 = "77"
  • 输出:"533"
  • 解释:个位 6+7=13(留 3 进 1),十位 5+7+1=13(留 3 进 1),百位 4+0+1=5,结果为 "533"(含进位的典型场景)。
  • 输入:num1 = "999999999999999999", num2 = "1"
  • 输出:"1000000000000000000"
  • 解释:超长大数相加,末尾进位贯穿所有位,最终需在最前方补 1(大数计算核心边界场景)。
  • 输入:num1 = "0", num2 = "0"
  • 输出:"0"
  • 解释:两个零相加,结果仍为零,注意不能返回 "00" 这类前导零(特殊边界场景)。

3. 难度级别

🟢 简单 → 🔵 中等(实际考察):题目逻辑本身不复杂,但大数场景下的「进位传递」「长度对齐补零」「末尾残留进位」这三个点极易出错,核心是复刻竖式加法的完整流程,确保覆盖所有大数计算的边界情况。

二、解题思路大剖析

1. 基础解法:模拟大数竖式相加

基础解法的核心思路就是复刻大数竖式加法的手工流程:因为是大数,无法直接转数字计算,所以从两个字符串的「末尾(个位)」开始,逐位提取数字相加,同步记录当前位结果和进位,最后将结果反转(因计算顺序是从低位到高位)。

核心步骤:

  1. 指针初始化:i 指向 num1 末尾(个位),j 指向 num2 末尾(个位),适配大数的低位到高位计算逻辑;

  2. 进位初始化:carry = 0(初始无进位,大数相加的进位可能贯穿多位);

  3. 结果容器:用数组 res 存储每一位结果(大数拼接频繁,数组比字符串高效);

  4. 循环计算(覆盖大数所有位 + 残留进位):只要 i >= 0(num1 未处理完)、j >= 0(num2 未处理完)或 carry > 0(仍有进位),就继续:

    • 提取当前位数字:num1 当前位为 i >= 0 ? num1[i] - '0' : 0(大数长度不一致时,短数高位补 0),num2 同理;
    • 计算当前位总和:sum = 位1 + 位2 + carry(必须包含前一位进位,大数进位不可遗漏);
    • 提取当前位结果:sum % 10(取个位,如 sum=13 则当前位为 3);
    • 更新进位:carry = Math.floor(sum / 10)(取十位,如 sum=13 则进位为 1,可能传递到下一位);
    • 存入结果:将当前位结果推入 res 数组;
    • 指针左移:i--j--,处理大数的更高位;
  5. 结果整理:res 中是「个位→高位」的顺序,反转后拼接成字符串(大数的高位在前、低位在后)。

分步拆解演示(以大数输入 num1="9999", num2="123" 为例):

  • 初始状态:i=3(num1[3]='9'),j=2(num2[2]='3'),carry=0res=[]

  • 第 1 轮(个位):

    • 位 1=9,位 2=3 → sum=9+3+0=12;
    • 当前位:12%10=2 → res=[2];
    • 进位:12/10=1 → carry=1;
    • 指针:i=2,j=1;
  • 第 2 轮(十位):

    • 位 1=9(num1 [2]='9'),位 2=2(num2 [1]='2') → sum=9+2+1=12;
    • 当前位:12%10=2 → res=[2,2];
    • 进位:12/10=1 → carry=1;
    • 指针:i=1,j=0;
  • 第 3 轮(百位):

    • 位 1=9(num1 [1]='9'),位 2=1(num2 [0]='1') → sum=9+1+1=11;
    • 当前位:11%10=1 → res=[2,2,1];
    • 进位:11/10=1 → carry=1;
    • 指针:i=0,j=-1;
  • 第 4 轮(千位):

    • 位 1=9(num1 [0]='9'),位 2=0(j<0 补 0) → sum=9+0+1=10;
    • 当前位:10%10=0 → res=[2,2,1,0];
    • 进位:10/10=1 → carry=1;
    • 指针:i=-1,j=-1;
  • 第 5 轮(残留进位):

    • 位 1=0,位 2=0 → sum=0+0+1=1;
    • 当前位:1%10=1 → res=[2,2,1,0,1];
    • 进位:1/10=0 → carry=0;
    • 指针:i=-1,j=-1;
  • 循环终止:i<0、j<0 且 carry=0;

  • 反转 res:[2,2,1,0,1] → [1,0,1,2,2] → 拼接成字符串 "10122"(9999+123=10122,符合大数计算预期)。

JavaScript 代码实现(基础解法):

js 复制代码
/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
var addStrings = function(num1, num2) {
    let i = num1.length - 1; // 指向num1末尾(大数个位)
    let j = num2.length - 1; // 指向num2末尾(大数个位)
    let carry = 0; // 进位,大数相加可能跨多位传递
    const res = []; // 存储每一位结果,避免大数字符串频繁拼接
    
    // 循环条件:覆盖大数所有位 + 残留进位
    while (i >= 0 || j >= 0 || carry > 0) {
        // 提取当前位数字(大数长度不一致时补0),字符转数字(减'0')
        const digit1 = i >= 0 ? num1[i] - '0' : 0;
        const digit2 = j >= 0 ? num2[j] - '0' : 0;
        
        // 计算当前位总和(含前一位进位)
        const sum = digit1 + digit2 + carry;
        // 当前位结果:sum的个位数
        const currentDigit = sum % 10;
        // 更新进位:sum的十位数(向下取整,可能为0或1)
        carry = Math.floor(sum / 10);
        
        // 推入结果数组(大数低位→高位顺序)
        res.push(currentDigit);
        
        // 指针左移,处理大数更高位
        i--;
        j--;
    }
    
    // 反转数组→拼接字符串(大数高位→低位顺序)
    return res.reverse().join('');
};

// 测试用例验证(覆盖大数、进位、边界场景)
console.log(addStrings("11", "123")); // 输出"134",符合预期
console.log(addStrings("9999", "123")); // 输出"10122",符合预期
console.log(addStrings("999999999999999999", "1")); // 输出"1000000000000000000",符合预期
console.log(addStrings("0", "0")); // 输出"0",符合预期

基础解法的优缺点:

  • 优点:完全贴合大数竖式加法逻辑,步骤清晰,覆盖所有大数场景的边界(超长长度、跨位进位、残留进位),面试中写出来稳定性高,不易出错;
  • 缺点:代码有少量冗余变量(如 digit1 digit2),但不影响可读性,大数计算场景下时间和空间已接近最优,无明显可优化点。

2. 优化解法:代码简洁化

优化解法的核心逻辑和基础解法完全一致(仍是模拟大数竖式),仅在代码写法上精简,减少冗余变量,让代码更紧凑(面试中能体现对大数计算逻辑的熟练掌握)。

优化点:

  • 合并变量声明:将 i j carry 合并声明,减少代码行数;
  • 嵌入位计算:将 digit1 digit2 的提取直接嵌入 sum 计算中,避免冗余变量;
  • 简化循环条件:carry 为 0 时会自动终止,无需写 carry > 0(因 0 为 falsy 值)。

JavaScript 代码实现(优化解法):

js 复制代码
/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
var addStrings = function(num1, num2) {
    let i = num1.length - 1, j = num2.length - 1, carry = 0;
    const res = [];
    
    while (i >= 0 || j >= 0 || carry) {
        // 直接计算当前位总和(嵌入大数位提取+补0逻辑)
        const sum = (i >= 0 ? num1[i] - '0' : 0) + (j >= 0 ? num2[j] - '0' : 0) + carry;
        res.push(sum % 10); // 当前位结果
        carry = Math.floor(sum / 10); // 更新进位
        i--;
        j--;
    }
    
    return res.reverse().join('');
};

// 测试用例验证
console.log(addStrings("456", "77")); // 输出"533",符合预期
console.log(addStrings("999999999999999999", "1")); // 输出"1000000000000000000",符合预期

优化解法的特点:

  • 逻辑不变:完全遵循大数竖式加法规则,覆盖所有边界场景;
  • 代码精炼:行数减少,无冗余变量,面试时书写速度更快;
  • 可读性强:变量名自解释,面试官能快速理解大数计算逻辑;
  • 复杂度不变:时间和空间复杂度与基础解法一致,属于「写法优化」而非「算法优化」。

三、总结

1. 核心逻辑

大数相加(字符串版)的本质是「模拟手工竖式加法」,核心要点有三个,缺一不可:

  1. 「从后往前算」:大数的低位在字符串末尾,需从末尾开始逐位处理;
  2. 「补零对齐」:大数长度不一致时,短数的高位补 0,避免索引越界,确保每一位都能对应相加;
  3. 「进位不遗漏」:每一步相加必须带上前一位的进位,且循环结束前需检查是否有残留进位(如 999+1 的最后进位 1)。

2. 最后

今天的「大数相加(字符串版)」就讲解到这里啦!相信大家已经吃透了「模拟竖式 + 进位传递」的核心逻辑,不管是基础解法还是优化解法,都能轻松应对面试中的大数场景。如果在测试超长大数、全 9 数字相加等特殊情况时遇到问题,或者有更巧妙的实现思路,欢迎在评论区留言讨论~

下一篇,咱们会继续攻克 LeetCode 高频题(「三数之和」),关注我,刷题路上不迷路!咱们下期再见~ 👋

相关推荐
会编程的土豆2 小时前
leetcode hot 100 之哈希
算法·leetcode·哈希算法
假面骑士阿猫2 小时前
TRAE配置OpenSpec实现SDD规范驱动开发
前端·人工智能·代码规范
qwehjk20082 小时前
分布式计算C++库
开发语言·c++·算法
m0_716765232 小时前
C++提高编程--仿函数、常用遍历算法(for_each、transform)详解
java·开发语言·c++·经验分享·算法·青少年编程·visual studio
哈哈哈哈哈哈哈哈8532 小时前
WSL + Tailscale 导致 apt update 卡 0% 的解决方案
前端
JYeontu2 小时前
程序员都是这样剪视频的?
前端
小雨cc5566ru2 小时前
基于Nodejs+vue+ElementUI的大学生课程排课管理系统设计
前端·vue.js·elementui
qq_8406122332 小时前
Nodejs+vue+ElementUI框架的家政服务评价系统 保洁员预约系统的设计与实现
前端·vue.js·elementui
计算机应用技术三班-欧婷2 小时前
ElementUI从入门到实战全攻略
前端·vue.js·ui