题目概览
范围
| 来源 | 内容 |
|---|---|
class010 | MergeTwoLists |
题型归纳
题型: 链表双指针
题目特征: 两个链表都已经按非递减顺序排列,需要把所有节点重新串成一个有序链表。
解法: 每次比较两个链表当前节点的值,把较小的节点接到结果链表后面。
易错点: 处理空链表、确定新头节点、移动 pre 指针、最后接上剩余链表。
看到”两个链表都已有序、合并为一个有序链表”就是双指针扫描的信号。核心做法是先选出两个头中较小的作为新头,然后用
pre、cur1、cur2三个指针同步推进;最容易出错的是循环体内漏写pre = pre.next,或者循环结束后逐个处理剩余节点而不是直接整体接上。
例题:21. 合并两个有序链表
题意
给定两个按非递减顺序排列的链表 list1 和 list2,将它们合并成一个新的非递减链表并返回。
新链表由原来两个链表中的节点拼接而成,不需要创建新的节点。
示例
示例 1:
![合并示例:[1,2,4] 和 [1,3,4] 合并为 [1,1,2,3,4,4]](https://assets.leetcode.com/uploads/2020/10/03/merge_ex1.jpg)
输入: list1 = [1,2,4], list2 = [1,3,4]
输出: [1,1,2,3,4,4]
示例 2:
输入: list1 = [], list2 = []
输出: []
示例 3:
输入: list1 = [], list2 = [0]
输出: [0]
提示
- 两个链表的节点数目范围是
[0, 50] -100 <= Node.val <= 100list1和list2均按非递减顺序排列
解法
核心思路
因为两个链表本身已经有序,所以只需要从两个链表的头节点开始比较,把较小的节点接到结果链表后面,被接走的链表指针向后移动一位,pre 始终指向结果链表的最后一个节点。
这里不使用虚拟头节点,而是先比较 head1 和 head2,选出较小的作为真正的头节点 head,再用 cur1 和 cur2 从各自起点开始扫描。
两个链表都有序,所以剩余部分的所有节点必然大于等于已合并部分的尾部——循环结束后直接整体接上,不需要逐个处理。

核心模板
public static ListNode mergeTwoLists(ListNode head1, ListNode head2) { if (head1 == null || head2 == null) { return head1 == null ? head2 : head1; }
// 谁小谁做头节点 ListNode head = head1.val <= head2.val ? head1 : head2; ListNode cur1 = head.next; ListNode cur2 = head == head1 ? head2 : head1; ListNode pre = head;
while (cur1 != null && cur2 != null) { // 谁小接谁 if (cur1.val <= cur2.val) { pre.next = cur1; cur1 = cur1.next; } else { pre.next = cur2; cur2 = cur2.next; } pre = pre.next; }
// 剩余部分直接整体接上 pre.next = cur1 != null ? cur1 : cur2; return head;}变量角色
head 是合并后链表的真正头节点,最终返回它;cur1 从选为头节点的那条链表的 head.next 开始走,cur2 从另一条链表的头节点开始走;pre 始终指向结果链表当前的尾节点,每接上一个新节点就跟着往后移动一位。
过程拆解
- 空链表特判:任一为空直接返回另一个,不进入主逻辑。
- 选头节点:比较
head1.val和head2.val,较小的作为head;cur1从head.next出发,cur2从另一条链表头出发。 - 双指针扫描:
cur1和cur2都不为空时,把较小的接到pre.next,对应指针后移,pre跟着移动。 - 收尾:循环结束后至少一条为空,把不为空的那条直接接到
pre.next。
易错点
- 任一链表为空时要提前返回,不能让
cur2 = head == head1 ? head2 : head1这行拿到null后继续参与比较。 cur1从head.next开始,而不是head——head已经是结果链表的头节点,不能再被扫描一次。
循环体内漏写pre = pre.next错误写法:
if (cur1.val <= cur2.val) {pre.next = cur1;cur1 = cur1.next;} else {pre.next = cur2;cur2 = cur2.next;}// 漏写 pre = pre.next为什么错:
pre始终停在第一个接入的节点,之后每轮都覆盖同一个pre.next,最终结果链表只剩最后一次接上的那个节点,前面的全部丢失。正确写法:
if (cur1.val <= cur2.val) {pre.next = cur1;cur1 = cur1.next;} else {pre.next = cur2;cur2 = cur2.next;}pre = pre.next; // 关键:pre 跟着新接入的节点往后走
口诀先选头,
pre跟着走,循环后剩余直接接。
复杂度
- 时间复杂度:
O(n + m),n和m分别是两个链表的长度 - 空间复杂度:
O(1)
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时


















