Skip to main content

合并两个有序链表

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

示例 1:

image

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

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

输入:l1 = [], l2 = [0] 输出:[0]

进阶做法: 要合并两个升序链表为一个新的升序链表,可以使用递归或迭代的方法。以下是两种方法的详细实现:

方法一:递归法

核心思路
比较两个链表的当前节点,选择较小的节点作为合并后的头节点,递归处理剩余节点。

步骤

  1. 终止条件:若其中一个链表为空,直接返回另一个链表。
  2. 递归比较
    • list1.val ≤ list2.val,选择 list1 为头节点,递归合并 list1.nextlist2
    • 否则,选择 list2 为头节点,递归合并 list1list2.next

代码实现

function mergeTwoLists(list1, list2) {
if (!list1) return list2;
if (!list2) return list1;

if (list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}

复杂度分析

  • 时间复杂度:O(m + n),其中 m 和 n 分别为两个链表的长度。
  • 空间复杂度:O(m + n),递归栈深度最大为链表长度之和。

方法二:迭代法(推荐)

核心思路
使用虚拟头节点(dummy node)和指针遍历,逐个比较两链表节点,按升序连接。

步骤

  1. 创建虚拟头节点:用于简化边界处理。
  2. 遍历两链表
    • 比较当前节点,将较小节点连接到结果链表。
    • 移动指针继续比较,直到其中一个链表遍历完毕。
  3. 连接剩余节点:将另一个链表的剩余部分直接连接到结果链表末尾。

代码实现

function mergeTwoLists(list1, list2) {
const dummy = new ListNode(-1); // 虚拟头节点
let current = dummy; // 当前指针

while (list1 && list2) {
if (list1.val <= list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next; // 移动当前指针
}

// 连接剩余部分
current.next = list1 || list2;

return dummy.next; // 返回虚拟头节点的下一个节点(真正的头节点)
}

复杂度分析

  • 时间复杂度:O(m + n),需遍历两链表各一次。
  • 空间复杂度:O(1),仅需常数级额外空间。

示例验证

输入链表:

  • list1: 1 → 2 → 4
  • list2: 1 → 3 → 4

迭代法过程

  1. 初始化dummy = -1current = -1
  2. 比较节点
    • 1 ≤ 1 → 连接 list11current → 1list1 → 2
    • 2 > 1 → 连接 list21current → 1 → 1list2 → 3
    • 2 ≤ 3 → 连接 list12current → 1 → 1 → 2list1 → 4
    • 4 ≤ 3 → 连接 list23current → 1 → 1 → 2 → 3list2 → 4
    • 4 ≤ 4 → 连接 list14current → 1 → 1 → 2 → 3 → 4list1 → null
  3. 连接剩余节点list2 剩余 4,连接后结果为 1 → 1 → 2 → 3 → 4 → 4

总结

  • 递归法:代码简洁,但可能导致栈溢出,适用于链表较短的场景。
  • 迭代法:空间效率更高,推荐使用。