力扣hot100之2:两数相加
力扣hot100之2:两数相加

LeetCode 2:两数相加(Add Two Numbers)题解

Add Two Numbers 是链表题里的经典入门题,也是一道非常适合练习“哑节点 + 逐位处理 + 进位”的题目。

题目大意是:

给你两个非空链表,表示两个非负整数。数字按照逆序存储,每个节点只存储一位数字。请你将这两个数相加,并以相同形式返回一个表示和的链表。


题目示例

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]

解释:

  • l1 表示数字 342

  • l2 表示数字 465

  • 342 + 465 = 807

因为题目要求结果也按逆序存储,所以返回链表 [7,0,8]


解题思路

这道题本质上就是模拟我们平时做加法的过程:

  1. 从两个链表的头节点开始逐位相加

  2. 需要考虑上一位产生的进位 carry

  3. 当前位的结果是 sum % 10

  4. 新的进位是 sum / 10

  5. 一直处理到两个链表都遍历结束

  6. 如果最后还有进位,需要再补一个节点


为什么要用哑节点(dummyHead)?

在链表题中,哑节点是一个非常常见的小技巧。

它的作用是:

  • 统一链表头节点的处理逻辑

  • 避免单独判断“第一个节点怎么创建”

  • 最后直接返回 dummyHead.next 即可

这样代码会更简洁,也更不容易出错。


Java 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0);
        ListNode p = l1, q = l2, curr = dummyHead;
        int carry = 0;

        while (p != null || q != null) {
            int x = (p != null) ? p.val : 0;
            int y = (q != null) ? q.val : 0;
            int sum = carry + x + y;

            carry = sum / 10;
            curr.next = new ListNode(sum % 10);
            curr = curr.next;

            if (p != null) p = p.next;
            if (q != null) q = q.next;
        }

        if (carry > 0) {
            curr.next = new ListNode(carry);
        }

        return dummyHead.next;
    }
}

代码拆解

1. 初始化变量

ListNode dummyHead = new ListNode(0);
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;

这里定义了几个关键变量:

  • dummyHead:结果链表的哑节点

  • p:用于遍历链表 l1

  • q:用于遍历链表 l2

  • curr:始终指向结果链表的末尾

  • carry:记录进位


2. 同时遍历两个链表

while (p != null || q != null)

这里用的是“或”条件,不是“且”。

原因很简单:

  • 只要有一个链表还没遍历完,就还需要继续计算

  • 如果某个链表已经为空,就把该位当成 0


3. 取当前位的值

int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;

如果当前节点存在,就取它的值;如果不存在,就补 0

这一步可以很好地处理两个链表长度不一致的情况。


4. 计算当前位和进位

int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);

这里是整道题的核心:

  • sum:当前位总和

  • sum % 10:当前位要存入新链表的值

  • sum / 10:新的进位

例如:

  • 8 + 7 = 15

  • 当前位存 5

  • 进位变成 1


5. 指针后移

curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;

每处理完一位后:

  • 结果链表尾指针向后移动

  • 原链表指针也继续向后遍历


6. 最后一位进位单独处理

if (carry > 0) {
    curr.next = new ListNode(carry);
}

这是一个非常容易遗漏的细节。

例如:

l1 = [9,9,9]
l2 = [1]

计算过程对应:

999 + 1 = 1000

结果应该是:

[0,0,0,1]

所以如果最后还有进位,一定要再补一个节点。


示例演示

以这组数据为例:

l1 = [2,4,3]
l2 = [5,6,4]

逐位计算过程如下:

第 1 位

  • 2 + 5 + 0 = 7

  • 当前位放 7

  • 进位 0

第 2 位

  • 4 + 6 + 0 = 10

  • 当前位放 0

  • 进位 1

第 3 位

  • 3 + 4 + 1 = 8

  • 当前位放 8

  • 进位 0

最终结果链表为:

[7,0,8]

复杂度分析

时间复杂度

O(max(m, n))

其中:

  • m 是链表 l1 的长度

  • n 是链表 l2 的长度

因为最多只遍历两个链表各一次。

空间复杂度

O(max(m, n))

原因是需要创建一个新的结果链表来保存答案。

如果只讨论额外辅助变量,那么辅助空间是 O(1) 但按照算法题通常的口径,返回结果链表本身也算空间,因此整体空间复杂度一般写作 O(max(m, n))


这道题的关键点

这题真正需要掌握的其实就三点:

  1. 用哑节点简化链表构造

  2. carry 处理进位

  3. 注意最后一位是否还需要补节点

只要这三点想清楚,代码实现并不复杂。


总结

Add Two Numbers 是一道非常经典的链表模拟题。

它考察的不是特别复杂的数据结构,而是你是否能把“加法过程”准确地转化成代码逻辑。

这类题做完以后,建议顺便总结一下两个常见技巧:

  • 链表题里的 dummyHead

  • 模拟题里的 进位 carry

这两个技巧在后面的很多题目里还会反复出现。


适合发布的结尾

如果你刚开始刷链表题,这道题很值得反复练习。

因为它同时训练了你这几项能力:

  • 链表遍历

  • 新链表构造

  • 边界条件处理

  • 进位模拟

把这题真正吃透之后,再去看很多链表中等题,会轻松不少。

如果对您有帮助的话,能否支持一下博主?
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇