跳到正文
第 2 题中等

两数相加

Add Two Numbers

两个非负整数按逆序存放在链表里,逐位相加,返回同样逆序存放的和。

在力扣中练习

题目描述

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

 

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

 

提示:

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

解题思路

主解法

逐位模拟 · 进位

像竖式加法一样,从低位到高位处理进位

链表是逆序存放的,头节点就是个位,正好符合竖式加法从低位算起的顺序。

每一位计算 a + b + carry,当前位写 sum % 10,新的进位是 sum / 10。用 dummy 哨兵挂结果链,tail 每次追加新节点。

分步图解

第 1 位:8 + 2 + 进位 0 = 10,写 0,新进位 1。

两个个位相加可能超过 9,超过的部分必须带到下一位。

carry
1
两个加数 · 低位在左值 · next 引用
nullnull8A0节点 A0,值 8,next → a-19A1节点 A1,值 9,next → a-2l19A2节点 A2,值 9,next → null2B0节点 B0,值 2,next → b-19B1节点 B1,值 9,next → nulll2
结果链 · 当前进位 1值 · next 引用
null哨兵dummy节点 dummy,值 哨兵,next → r-00个位节点 个位,值 0,next → nulltail
1 / 3
每一位都只保留个位,进位 1 带到下一位;连续进位不能提前清零。

自拟主例是 998 + 92:前两位连续进位,第三位 9 + 0 + 1 仍然进位;两个加数都走完后,carry 还是 1,循环条件必须包含 carry,才能补出最高位的 1。

分步图解

第 3 位:9 + 空 + 进位 1 = 10,写 0,新进位 1。

carry
1
两个加数 · 低位在左值 · next 引用
nullnull8A0节点 A0,值 8,next → a-19A1节点 A1,值 9,next → a-29A2节点 A2,值 9,next → null2B0节点 B0,值 2,next → b-19B1节点 B1,值 9,next → null
l1→ nulll2→ null
结果链 · 当前进位 1值 · next 引用
null哨兵dummy节点 dummy,值 哨兵,next → r-00个位节点 个位,值 0,next → r-19十位节点 十位,值 9,next → r-20百位节点 百位,值 0,next → nulltail
1 / 3
两个加数都为空时,carry 仍可能让循环多走一轮,产生新的最高位。

两条链长度不同时,短链先变空,后续按 0 处理即可,不需要提前补齐。整个过程每个节点只访问一次,结果节点也是边算边建。

时间 O(max(m, n))空间 O(1)(不计结果)

易错点

  • 循环条件只写 l1 || l2:最高位进位会丢,998 + 92 会得到 090 而不是 1090。
  • 先把 l1 指向 next 再读 val:当前位读到的就是下一个数。
  • 把链表转成 int 再相加:链表可能远超 64 位整数范围。
对照解法

递归 · 带进位返回

当前位建节点,剩下的位交给递归

写法更短,但递归深度等于较长链的长度,最坏需要 O(n) 调用栈;仅作对照。

递归把“处理当前位”和“处理剩余位”分开:先算 sum = carry + a + b,建当前节点,再递归处理 a->next、b->next 和新的进位。

终止条件是三个输入都为空/零:两个链表都走完且没有进位。这样最高位进位会自然多建一个节点。

它和迭代版的状态完全一致,只是把循环改成了调用栈。链表很长时递归可能溢出,工程上仍推荐迭代。

  1. 1若两个节点都为空且 carry 为 0,返回 null。
  2. 2计算当前和与进位,建立当前节点。
  3. 3递归处理剩余部分,接到当前节点后。
  4. 4返回当前节点。
时间 O(max(m, n))空间 O(max(m, n)) 调用栈

易错点

  • 终止条件漏掉 carry:最高位进位会丢。
  • 递归参数里继续使用已经移动过的指针。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入l1 = [8,9,9](998),l2 = [2,9](92)

carry = 0,结果链只有 dummy;l1、l2 指向两个数的最低位。

carry
0
两个加数 · 低位在左值 · next 引用
nullnull8A0节点 A0,值 8,next → a-1l19A1节点 A1,值 9,next → a-29A2节点 A2,值 9,next → null2B0节点 B0,值 2,next → b-1l29B1节点 B1,值 9,next → null
结果链 · 当前进位 0值 · next 引用
null哨兵dummy节点 dummy,值 哨兵,next → nulltail
1 / 5
查看 C++ 代码

延伸阅读

  • 逆序链表 + 进位是“大数加法”的常见模型;如果题目改成顺序存放,需要先反转或借助栈。
  • 哨兵节点在“结果长度未知、需要不断追加”的场景里特别省事。