1. 题目链接
2. 题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
3. 题目示例
示例 1 :
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2 :
输入:lists = []
输出:[]
4. 解题思路
- 问题分解:
- 将合并K个链表的问题分解为多个合并两个链表的问题
- 采用分治策略,递归地将链表数组分成两半
- 关键步骤:
- 分治框架:
- 基本情况:当范围为空返回null,单个链表直接返回
- 递归处理左右两半
- 合并左右结果
- 合并操作:使用经典的合并两个有序链表算法
- 分治框架:
- 优化点:
- 避免顺序合并导致的重复遍历(如1+2,然后(1+2)+3…)
- 分治策略使合并过程更平衡,减少重复工作
5. 题解代码
class Solution {
// 主方法:合并K个有序链表
public ListNode mergeKLists(ListNode[] lists) {
// 调用分治方法,初始范围是整个数组
return mergeKLists(lists, 0, lists.length);
}
// 分治方法:合并lists[i]到lists[j-1]的链表
private ListNode mergeKLists(ListNode[] lists, int i, int j) {
int m = j - i; // 计算当前范围内链表数量
// 基本情况处理
if (m == 0) { // 空数组情况
return null;
}
if (m == 1) { // 只有一个链表,直接返回
return lists[i];
}
// 分治递归
ListNode left = mergeKLists(lists, i, i + m / 2); // 合并左半部分
ListNode right = mergeKLists(lists, i + m / 2, j); // 合并右半部分
// 合并左右两部分结果
return mergeTwoLists(left, right);
}
// 辅助方法:合并两个有序链表(21题解法)
private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(); // 哨兵节点
ListNode cur = dummy; // 当前合并位置
// 双指针遍历
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
cur.next = list1;
list1 = list1.next;
} else {
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
// 处理剩余节点
cur.next = (list1 != null) ? list1 : list2;
return dummy.next;
}
}
6. 复杂度分析
- 时间复杂度:O(NlogK)
- 每次合并操作时间复杂度为O(N),其中N是总节点数
- 分治树高度为logK(K是链表数量)
- 综合:O(NlogK)
- 空间复杂度:O(logK)
- 递归调用栈深度为logK
- 合并操作使用常数空间