两数相加 图解题解
链表里藏着两个大数,逐位相加还要处理进位——模拟竖式,一遍搞定。
两数相加就是在做竖式加法,只是数字从个位开始顺着链表排好了:两个指针对齐从表头(个位)同步往后走,每位把 a + b + carry 算出来,对 10 取余写进新节点,整除 10 的进位带给下一位;哪条链走完了那位就当 0,最后若还剩进位就补一个节点——一遍扫完不回头。
这道题到底在问什么
- 输入
- A=846729583, B=358496857
- 输出
- 1205226440
最优解:为什么这么做
一句话答案:LeetCode 2 两数相加的标准解法是模拟竖式加法:两条链表低位在前,同步遍历逐位取数,加上进位变量 carry,当前位写 (a + b + carry) % 10,新进位取十位,循环条件是 l1、l2、carry 三者任一还有剩。时间 O(max(m,n))、空间 O(max(m,n)) 用于结果链表。
这道题真正在问什么
两个非负整数各用一条链表表示,数位按「低位在前」存放(个位是头节点),要求返回它们的和,同样用低位在前的链表表示。数字可能非常长,长到任何整数类型都装不下——所以别想着把链表还原成数字再相加,必须像小学竖式那样逐位处理。
为什么低位在前反而是送分设计
竖式加法天然从个位算起,因为低位产生的进位要传给高位。链表低位在前,意味着从两条链的头节点开始同步往后走,遍历顺序恰好就是加法需要的计算顺序,一遍顺序遍历直接出结果,连反转都不用。如果题目改成高位在前(LeetCode 445),反而要先用栈或反转把顺序倒过来才能加。
想通这一点,整道题就从「链表技巧题」还原成它的本质:用链表外壳包着的高精度加法模拟。
进位 carry 为什么是整个算法的核心
逐位相加时,各位之间唯一的耦合就是进位:本位的和超过 9,要往高一位送一个 1。把这件事收敛成一个变量 carry,每一位的计算就完全统一:总和 = a + b + carry,当前位写总和 % 10,新的 carry 是总和 // 10。由于每位相加最大是 9 + 9 + 1 = 19,carry 只会是 0 或 1,永远不会连环爆位。
正确性不需要额外论证——这就是竖式加法本身的规则,算法只是把人手算的过程逐字翻译成代码。真正的功夫全在边界处理上。
循环条件为什么要带上 carry
两条链长度可能不同,短的先走完后,缺的位当 0 参与运算即可,不需要补节点。但最容易漏的一种情况是:两条链都走完了,carry 还是 1——比如 5 + 5,两条链各一个节点,加完后必须再补一个最高位节点存这个 1,否则 10 会被算成 0。
把循环条件写成「l1 或 l2 或 carry 任一非空非零就继续」,最后这次补位就被自然纳入循环,不用在循环外单独写一段收尾代码。这是本题第一坑,也是面试官检查你是否真正模拟过竖式的试金石。
哑结点、复杂度与常见变体
结果链表的头节点在循环第一轮才产生,所以照例用哑结点 dummy 起头,接第一个节点和接后续节点写法一致,最后返回 dummy.next。
时间 O(max(m,n)):两条链各扫一遍,循环轮数由较长的链加上可能的一次补位决定。空间 O(max(m,n)):结果链表最长比长链多一个节点;除结果外只用了 carry 一个额外变量。变体方面,高位在前的版本先把两条链压栈再从栈顶逐位相加、用头插法建结果,核心的 carry 逻辑与本题完全相同。
▶ 动画逐步走查(共 23 步)——想跟着动画一帧帧对照就展开
- 3记住这条,下面每一位都在套它。
- 4上两行是 A、B(低位在前),最下一行是结果链表,先留空。我们用一个进位变量 carry 从 0 开始。
- 5两个指针 p、q 分别指向 A、B 的头(个位),cur 指向要写的结果位。carry=0,开始逐位相加。
- 6第 0 位:a=3,b=7,进位 carry=0。cur = 3+7+0 = 10。
- 710 % 10 = 0 写进结果第 0 位;10 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 8第 1 位:a=8,b=5,进位 carry=1。cur = 8+5+1 = 14。
- 914 % 10 = 4 写进结果第 1 位;14 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 10第 2 位:a=5,b=8,进位 carry=1。cur = 5+8+1 = 14。
- 1114 % 10 = 4 写进结果第 2 位;14 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 12第 3 位:a=9,b=6,进位 carry=1。cur = 9+6+1 = 16。
- 1316 % 10 = 6 写进结果第 3 位;16 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 14第 4 位:a=2,b=9,进位 carry=1。cur = 2+9+1 = 12。
- 1512 % 10 = 2 写进结果第 4 位;12 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 16第 5 位:a=7,b=4,进位 carry=1。cur = 7+4+1 = 12。
- 1712 % 10 = 2 写进结果第 5 位;12 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 18第 6 位:a=6,b=8,进位 carry=1。cur = 6+8+1 = 15。
- 1915 % 10 = 5 写进结果第 6 位;15 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 20第 7 位:a=4,b=5,进位 carry=1。cur = 4+5+1 = 10。
- 2110 % 10 = 0 写进结果第 7 位;10 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 22第 8 位:a=8,b=3,进位 carry=1。cur = 8+3+1 = 12。
- 2312 % 10 = 2 写进结果第 8 位;12 >= 10 ? 是 → 进位 carry 变为 1,带到下一位。
- 24A、B 都走完了,但 carry 还是 1!别漏:要再补一个最高位节点 1。
- 25补上最高位 1,结果链表完成:1205226440(即 1205226440)。
⚠️ 容易写错的地方
✗ 错:循环只判 l1/l2
✓ 对:while 条件带上 carry
最高位进位会漏(如 5+5 应得 10)
✗ 错:忘了 carry 跨位累加
✓ 对:cur = a+b+carry
每位都要带上一位的进位
✗ 错:头节点特判一堆 if
✓ 对:用哑结点 dummy
统一处理,代码更干净
完整代码(Python / C++ / Java)
Python
def addTwoNumbers(l1, l2):
dummy = cur = ListNode(0) # 哑结点
carry = 0
while l1 or l2 or carry:
a = l1.val if l1 else 0
b = l2.val if l2 else 0
carry, d = divmod(a + b + carry, 10)
cur.next = ListNode(d); cur = cur.next
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummy.nextC++
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2){
ListNode dummy(0), *cur = &dummy;
int carry = 0;
while(l1 || l2 || carry){
int s = (l1?l1->val:0) + (l2?l2->val:0) + carry;
carry = s / 10;
cur->next = new ListNode(s % 10); cur = cur->next;
if(l1) l1 = l1->next;
if(l2) l2 = l2->next;
}
return dummy.next;
}Java
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0), cur = dummy;
int carry = 0;
while (l1 != null || l2 != null || carry != 0) {
int s = (l1 != null ? l1.val : 0) + (l2 != null ? l2.val : 0) + carry;
carry = s / 10;
cur.next = new ListNode(s % 10);
cur = cur.next;
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;
}
return dummy.next;
}复杂度
时间
O(max(m,n))
两表各扫一遍
空间
O(max(m,n))
结果链表长度
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 两数相加 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
如果数字是「高位在前」(LC445) 怎么办?+
先把两条链表入栈(或反转),再从栈顶逐位相加,结果用头插法构建。
为什么用哑结点?+
结果链表头节点在循环里才产生,哑结点让「接第一个节点」和「接后续节点」写法一致,免去特判。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 两数相加 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。