【LeetCode 热题 100】2. 两数相加——(解法一)递归法

Problem: 2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

整体思路

这段代码旨在解决一个经典的链表问题:两数相加 (Add Two Numbers)。问题要求将两个由链表表示的非负整数相加,其中链表的每个节点包含一个数字,并且数字是逆序存储的。例如,链表 2 -> 4 -> 3 代表数字 342。

该实现采用了一种非常优雅的 递归 方法来模拟逐位相加的过程。其核心逻辑如下:

  1. 递归函数设计

    • 定义一个辅助的递归函数 addTwo(l1, l2, carry),它接收两个链表节点和上一位的进位 carry作为参数。
    • 该函数的作用是计算当前位的和,并递归地调用自身来处理下一位的相加,最终返回构建好的结果链表的当前节点。
  2. 递归的终止条件 (Base Case)

    • if (null == l1 && null == l2 && 0 == carry):这是递归的最终出口。当两个输入链表都已遍历完毕(都为 null),并且没有来自上一位的进位时,说明加法过程彻底结束,应该返回 null,表示结果链表的末尾。
  3. 递归的递推关系 (Recursive Step)

    • 计算当前位的和int s = carry;。首先将和 s 初始化为上一位的进位。然后,如果 l1l2 不为空,就将其节点值加到 s 中,并将其指针后移一位。
    • 创建新节点:计算出的总和 s 可能大于9。当前位的值应该是 s % 10。因此,创建一个新节点 new ListNode(s % 10, ...)
    • 递归调用:新节点的 next 指针应该指向下一位加法的结果。这个结果通过递归调用 addTwo(l1, l2, s / 10) 来获得。这里的 s / 10 就是要传递给下一位的新的进位
    • 返回当前节点:函数返回新创建的、已连接好后续部分的节点。

通过这种自顶向下的递归方式,代码从链表的头部(个位)开始,逐位向高位处理,自然地构建出结果链表。

完整代码

/**
 * Definition for singly-linked list.
 */
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 {
    /**
     * 主函数,启动两数相加的过程。
     * @param l1 第一个数对应的链表
     * @param l2 第二个数对应的链表
     * @return 相加结果对应的链表
     */
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 初始调用递归函数,进位为 0
        return addTwo(l1, l2, 0);
    }

    /**
     * 递归辅助函数,执行一位的相加并链接后续结果。
     * @param l1 当前 l1 的节点
     * @param l2 当前 l2 的节点
     * @param carry 来自上一位的进位
     * @return 构建好的当前节点
     */
    private ListNode addTwo(ListNode l1, ListNode l2, int carry) {
        // 递归终止条件:两个链表都已遍历完,且没有进位
        if (null == l1 && null == l2 && 0 == carry) {
            return null;
        }
        
        // 计算当前位的总和,初始为上一位的进位
        int s = carry;
        // 如果 l1 链表还有节点,加上其值,并移动指针
        if (null != l1) {
            s += l1.val;
            l1 = l1.next;
        }
        // 如果 l2 链表还有节点,加上其值,并移动指针
        if (null != l2) {
            s += l2.val;
            l2 = l2.next;
        }
        
        // 创建新节点:
        // 节点的值是和的个位数 (s % 10)
        // 节点的 next 指针是递归调用处理下一位的结果,
        // 将新的进位 (s / 10) 传递下去。
        return new ListNode(s % 10, addTwo(l1, l2, s / 10));
    }
}

时空复杂度

时间复杂度:O(max(M, N))

  1. 递归深度:递归函数 addTwo 的调用次数由较长的链表决定。如果 l1 的长度是 Ml2 的长度是 N,那么递归的深度最多是 max(M, N) 次(如果最后还有进位,则再多一次)。
  2. 每次操作:在每次递归调用中,执行的都是常数时间的加法、取模、除法和指针移动操作。

综合分析
算法的时间复杂度与两个链表中较长者的长度成线性关系。因此,时间复杂度为 O(max(M, N))

空间复杂度:O(max(M, N))

  1. 递归栈空间:由于使用了递归,函数调用会占用调用栈的空间。递归的深度是 max(M, N),所以递归栈所需的空间复杂度是 O(max(M, N))
  2. 结果链表空间:算法创建了一个新的链表来存储结果。这个新链表的长度也是 max(M, N)(可能加1)。这部分空间通常被视为输出,不计入辅助空间,但它确实是算法使用的内存。

综合分析
算法的额外空间主要由递归调用栈决定。因此,空间复杂度为 O(max(M, N))

参考灵神

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xumistore

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值