R / Richie全部文章 ↑

Python · 2 分钟阅读

02:两数相加

目录


题目

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

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

示例

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

约束

  • 每个链表中的节点数范围 [1, 100]
  • 0 ≤ Node.val ≤ 9
  • 题目数据保证列表表示的数字不含前导零(除数字 0 本身外)

思路

链表是 逆序 存储的(个位在最前面),这刚好方便我们 从左到右模拟竖式加法:
从最低位开始相加,记录进位 carry,构造新节点。

⚠️ 不要 把链表先转成字符串再 int() 求和:

  • 链表的“数字”可能超过 64 位整数范围,会溢出;
  • 字符串转换多了一道开销,违背题目考察链表遍历的本意。

代码实现

from __future__ import annotations
from typing import Optional


class ListNode:
    """单链表节点。"""

    def __init__(self, val: int = 0, next: Optional["ListNode"] = None) -> None:
        self.val = val
        self.next = next


class Solution:
    def addTwoNumbers(
        self, l1: Optional[ListNode], l2: Optional[ListNode]
    ) -> Optional[ListNode]:
        dummy = ListNode(0)     # 哨兵节点,统一处理头节点逻辑
        cur = dummy
        carry = 0

        # 三个退出条件:l1、l2 任一未走完,或仍有进位
        while l1 or l2 or carry:
            v1 = l1.val if l1 else 0
            v2 = l2.val if l2 else 0
            total = v1 + v2 + carry
            carry, digit = divmod(total, 10)

            cur.next = ListNode(digit)
            cur = cur.next

            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None

        return dummy.next

单元测试

def build(nums: list[int]) -> Optional[ListNode]:
    dummy = ListNode(0)
    cur = dummy
    for n in nums:
        cur.next = ListNode(n)
        cur = cur.next
    return dummy.next


def to_list(node: Optional[ListNode]) -> list[int]:
    out = []
    while node:
        out.append(node.val)
        node = node.next
    return out


if __name__ == "__main__":
    s = Solution()
    assert to_list(s.addTwoNumbers(build([2, 4, 3]), build([5, 6, 4]))) == [7, 0, 8]
    assert to_list(s.addTwoNumbers(build([0]), build([0]))) == [0]
    assert to_list(s.addTwoNumbers(build([9, 9, 9]), build([1]))) == [0, 0, 0, 1]
    print("OK")

复杂度

  • 时间:O(max(N, M)),N / M 分别是两个链表的长度。
  • 空间:O(max(N, M)),新建的结果链表(不算 dummy 与输入链表)。

常见变体

变体 关键改动
链表是 正序 存储 先反转再相加,再反转输出
进阶:不修改原链表(就地相加) 复用 l1 或 l2 的节点
大数求和(不限于链表) 使用字符串逐位相加,处理任意长度的整数

易错点

  • 不要忘记 最后的进位(while ... or carry)。
  • carry, digit = divmod(total, 10) 是 Python 推荐的写法,比手算 % 10 清晰。
  • 使用 哨兵节点(dummy head)可以避免 head 单独处理的 if 分支。