跳到正文
第 21 题简单

合并两个有序链表

Merge Two Sorted Lists

把两条已经升序排列的链表合并成一条升序链表,节点直接复用。

在力扣中练习

题目描述

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

 

示例 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 均按 非递减顺序 排列

解题思路

主解法

哨兵 + 尾指针 · 迭代

每次取两个头中较小的那个,接到结果链尾

两条链已经各自有序,所以只需要反复比较当前两个头节点,把较小的那个摘下来接到结果链尾。

如果结果链一开始为空,第一个节点要单独判断;用 dummy 哨兵可以消掉这个分支:tail = &dummy,之后每次都是 tail->next = ...。

分步图解

dummy 指向空,tail 先停在 dummy;两条输入链的头由 list1、list2 指向。

list1 · 只移动头指针值 · next 引用
null1A0节点 A0,值 1,next → a-1list14A1节点 A1,值 4,next → a-26A2节点 A2,值 6,next → a-39A3节点 A3,值 9,next → null
list2 · 只移动头指针值 · next 引用
null2B0节点 B0,值 2,next → b-1list23B1节点 B1,值 3,next → b-26B2节点 B2,值 6,next → b-38B3节点 B3,值 8,next → b-410B4节点 B4,值 10,next → null
结果链 · 按顺序追加
哨兵dummy
dummy 只是入口哨兵,不属于答案;tail 标签标出当前结果链的尾节点。
1 / 3
dummy 让第一个节点不需要特殊处理,tail 始终指向结果链尾。

自拟主例里有重复值 6:一个来自 A、一个来自 B。它们值相同但身份不同,画面上用 A2、B2 区分;<= 让 A 的 6 先被取走,另一个随后跟上。

当某一条链先用完,另一条剩下的整段已经有序,直接挂到 tail 后面即可,不需要再逐个比较。

分步图解

9 ≤ 10,把 9(a-3)接到结果链尾;list1 后移。

list1 · 只移动头指针值 · next 引用
null1A0节点 A0,值 1,next → a-14A1节点 A1,值 4,next → a-26A2节点 A2,值 6,next → a-39A3节点 A3,值 9,next → null
list1→ null
list2 · 只移动头指针值 · next 引用
null2B0节点 B0,值 2,next → b-13B1节点 B1,值 3,next → b-26B2节点 B2,值 6,next → b-38B3节点 B3,值 8,next → b-410B4节点 B4,值 10,next → nulllist2
结果链 · 按顺序追加
哨兵dummy
1
2
3
4
6
6
8
9tail
dummy 只是入口哨兵,不属于答案;tail 标签标出当前结果链的尾节点。
1 / 3
一方用完后,另一方的剩余整段已经有序,直接接上即可。

全程只改 next,没有新建节点;时间复杂度与两条链总长度成正比。

时间 O(m + n)空间 O(1)

易错点

  • 返回 dummy 而不是 dummy.next:答案里会多出一个哨兵节点。
  • 每次忘记移动 tail:结果链的 next 被反复覆盖。
  • 剩下的一段仍用 while 逐个接:结果正确但多做无用的比较,也容易在空指针处出错。
对照解法

递归 · 归并

选出较小的头,剩下的交给递归

代码更短,但递归深度等于两条链总长度,最坏需要 O(m+n) 调用栈;链表很长时不如迭代稳。

递归写法的视角更小:如果 a 为空就返回 b,b 为空就返回 a;否则选出较小的头,把它的 next 指向“其余部分的合并结果”。

代码只有几行,和归并排序的 merge 完全同构。代价是递归深度等于两条链总长度,最坏 O(m+n) 的调用栈;链表很长时可能栈溢出。

如果题目没有额外限制,递归版本很适合快速表达思路;工程代码里更常用迭代版,避免深递归。

  1. 1任一链为空,直接返回另一条。
  2. 2比较两个头,选较小的作为当前节点。
  3. 3递归合并剩余部分,接到当前节点后面。
  4. 4返回当前节点。
时间 O(m + n)空间 O(m + n) 调用栈

易错点

  • 递归前没有保存 next:修改当前节点的 next 后,剩余链的入口会丢失。
  • 相等时没有稳定选择:虽然结果仍有序,但相同值的相对顺序会变化。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入list1 = [1,4,6,9],list2 = [2,3,6,8,10]

dummy 指向空,tail 先停在 dummy;两条输入链的头由 list1、list2 指向。

list1 · 只移动头指针值 · next 引用
null1A0节点 A0,值 1,next → a-1list14A1节点 A1,值 4,next → a-26A2节点 A2,值 6,next → a-39A3节点 A3,值 9,next → null
list2 · 只移动头指针值 · next 引用
null2B0节点 B0,值 2,next → b-1list23B1节点 B1,值 3,next → b-26B2节点 B2,值 6,next → b-38B3节点 B3,值 8,next → b-410B4节点 B4,值 10,next → null
结果链 · 按顺序追加
哨兵dummy
dummy 只是入口哨兵,不属于答案;tail 标签标出当前结果链的尾节点。
1 / 5
查看 C++ 代码

延伸阅读

  • 哨兵节点能统一“结果为空”和“结果非空”的处理,是链表构造题的常用起点。
  • 合并两条有序链是归并排序的核心步骤,第 148、23 题都会复用它。