【LeetCode热题100道笔记】合并两个有序链表

题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:
在这里插入图片描述
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:
输入:l1 = [], l2 = []
输出:[]

示例 3:
输入:l1 = [], l2 = [0]
输出:[0]

提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

思考一:迭代

通过引入哨兵节点(guard)简化头节点处理,使用双指针分别遍历两个有序链表,每次将值较小的当前节点连接到结果链表尾部,直至其中一个链表遍历完毕,最后拼接剩余节点,最终返回哨兵节点的后继节点即为合并后的有序链表。

算法过程

  1. 初始化:创建哨兵节点guard(用于简化头节点处理),定义指针p1、p2分别指向两个输入链表的头节点,指针p指向guard(用于构建结果链表)。
  2. 边界处理:若任一链表为空,直接返回另一链表(无需合并)。
  3. 遍历合并:当p1和p2均未遍历完时,循环执行:
    • 比较p1.val与p2.val,将值较小的节点连接到p.next。
    • 移动被选中节点的指针(p1或p2)及结果指针p。
  4. 拼接剩余节点:当其中一个链表遍历完后,将另一个链表的剩余部分直接连接到p.next。
  5. 返回结果:返回guard.next(哨兵节点的后继节点即为合并后链表的头节点)。

该算法时间复杂度为O(n+m)(n、m为两链表长度),空间复杂度为O(1)(仅使用常数级额外空间)。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    let p1 = list1;
    let p2 = list2;

    if (!p1 || !p2) {
        return p1 ? p1 : p2;
    }

    let guard = new ListNode();
    let p = guard;
    while (p1 || p2) {
        if (!p1) {
            p.next = p2;
            break;
        }
        if (!p2) {
            p.next = p1;
            break;
        }
        if (p1.val < p2.val) {
            p.next = p1;
            p1 = p1.next;
        } else {
            p.next = p2;
            p2 = p2.next;
        }
        p = p.next;
    }
   
    return guard.next;
};

思考二:递归

递归解法的核心是将"合并两个有序链表"的问题分解为更小的同类子问题:

  1. 若其中一个链表为空,直接返回另一个链表(递归终止条件)
  2. 否则比较两个链表当前头节点的值,选择值较小的节点作为合并后链表的头节点
  3. 被选中节点的next指针应指向"该节点的next部分与另一个链表"合并后的结果(递归调用)
  4. 最终返回选中的头节点,形成完整的合并链表

这种思路利用递归的自相似性,将原问题逐步简化,直到触及终止条件,再层层返回结果构建最终链表。

算法过程

  1. 终止条件判断

    • 若list1为空,返回list2
    • 若list2为空,返回list1
  2. 节点选择与递归

    • 若list1.val < list2.val:
      • 递归合并list1.next与list2,得到子结果
      • 将list1.next指向该子结果
      • 返回list1作为当前合并段的头节点
    • 否则:
      • 递归合并list1与list2.next,得到子结果
      • 将list2.next指向该子结果
      • 返回list2作为当前合并段的头节点
  3. 结果构建:通过递归调用的返回值,自动完成链表节点的正确连接,最终形成完整的有序链表

该算法的时间复杂度为O(n+m)(n和m分别为两个链表的长度),空间复杂度为O(n+m)(递归调用栈的深度)。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    let p1 = list1;
    let p2 = list2;

    if (!p1 || !p2) {
        return p1 ? p1 : p2;
    }

    let p3 = null;
    if (p1.val < p2.val) {
        p3 = mergeTwoLists(p1.next, p2);
        p1.next = p3;
        return p1;
    } else {
        p3 = mergeTwoLists(p1, p2.next);
        p2.next = p3;
        return p2;
    }   
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值