LeetCode 剑指 Offer 25. 合并两个排序的链表
题目描述



题意分析
把两条按值非递减排列的单链表合并成一条同样有序的链表,返回合并后的头节点。两条输入链中的所有节点都要保留,相同值允许重复出现,不能像集合合并那样去重。
输入可能有一条为空,也可能两条都为空。下面复用已有节点,通过调整
next重新连接结果,不为每个值重新创建节点;这会改变原来的链表连接关系。
解法:双指针归并链表
核心思路
[!blue]
用
l1和l2指向两条链表中尚未合并部分的头节点。由于两条剩余链表各自有序,它们所有未处理节点中的最小值,必然就在这两个头节点中。因此每次比较头节点,将较小者接到结果尾部,就能确定下一个位置,无需搜索链表内部。用哑节点
dummy固定结果入口,tail指向已经确定的结果前缀的最后一个节点。每次将选中的节点接到tail.next,只推进被选中的输入指针,再让tail移到刚接入的节点。未选择的一侧保持原位置,留待下一轮比较。从
dummy.next到tail的这段已确定前缀始终有序,而且最后一个值不大于任一剩余候选头。初始前缀为空时成立;每次接入当前最小候选后仍然成立,因此反复选择不会破坏结果顺序。相等时优先选择第一条链表只是固定取舍,另一条的同值节点仍会在后续保留。当一条链表耗尽,另一条剩余部分本身已经有序,所有值又不小于已经确定的尾节点,可以直接整段接上,不必再逐个访问。两条都为空时接上的也是空,返回
dummy.next即可统一处理空结果。
解题步骤
- 创建哑节点
dummy,令结果尾指针tail = dummy。- 两条剩余链表都非空时比较头值;第一条较小或相等就接入
l1,否则接入l2。- 只将被选择的一条输入链向后推进,再将
tail移到刚接入的节点。- 任一输入链为空后,把另一条剩余链整体接到
tail.next。- 返回
dummy.next,哑节点自身不属于答案。
代码实现
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
// 只推进被选中的输入链,再把结果尾移到新接节点
tail = tail.next;
}
// 一侧耗尽后,另一侧已整体有序,可直接连接
tail.next = l1 != null ? l1 : l2;
return dummy.next;
}
}
func mergeTwoLists(l1, l2 *ListNode) *ListNode {
dummy := &ListNode{}
tail := dummy
for l1 != nil && l2 != nil {
if l1.Val <= l2.Val {
tail.Next = l1
l1 = l1.Next
} else {
tail.Next = l2
l2 = l2.Next
}
// 只推进被选中的输入链,再把结果尾移到新接节点
tail = tail.Next
}
// 一侧耗尽后,另一侧已整体有序,可直接连接
if l1 != nil {
tail.Next = l1
} else {
tail.Next = l2
}
return dummy.Next
}
复杂度分析
- 时间复杂度:
O(m + n),其中m、n为两条输入链长度。每轮至少消耗一个节点,剩余链整体连接只需一次指针赋值。- 空间复杂度:
O(1)。复用输入节点,只增加一个哑节点和常数个指针。
关键点总结
[!green]
- 有序性把全体未处理节点的最小值限制在两个头节点中。
- 结果前缀与剩余输入通过各自指针表示,选择哪一侧就只消耗哪一侧。
- 剩余链已经满足排序关系,可以整体接尾,避免重复遍历。
- 哑节点统一处理首次接入和空输入,不应出现在返回链中。
易错点总结
[!yellow]
- 两个输入指针一起前进:每轮只接入一个节点,另一侧尚未选择的节点会被丢掉。
- 接入后忘记移动
tail:下一轮会覆盖同一个后继位置,导致已接入节点丢失。- 相等时跳过其中一个节点:合并需要保留两个输入中的全部出现次数,不是去重。
- 比较循环结束后直接返回:较长链表仍可能有未处理节点,应整体接到结果尾部。
- 返回
dummy而不是dummy.next:会在答案前增加一个不属于输入的节点。- 把此实现当作保留原链的复制:它重新使用原节点并调整连接,调用后原链结构不保证保持原样。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 23. 合并 K 个升序链表 | 困难 | 两链表归并是合并k条链表的基础子过程,可按分治成对合并。 |
| 88. 合并两个有序数组 | 简单 | 同样从两个有序来源取下一项,数组版本需避免覆盖尚未读取的数据,常从末尾合并。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!