【困难】题解力扣23:合并K个升序链表
题目详情
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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 = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按升序排列lists[i].length
的总和不超过10^4
解题思路
采用分治法 + 迭代合并策略高效合并 K 个有序链表:
- 边界处理:
- 若链表数组为空或长度为 0,直接返回
null
- 若只有一个链表,无需合并,直接返回该链表
- 分治合并:
- 初始化当前链表数量
n = lists.length
- 当
n > 1
时循环:- 计算新链表数量
newN = (n + 1) / 2
- 遍历链表数组,每次处理两个链表:
- 合并
lists[2*i]
和lists[2*i+1]
(若存在) - 将合并结果存回
lists[i]
位置
- 合并
- 更新
n = newN
进行下一轮合并
- 计算新链表数量
- 最终合并结果位于
lists[0]
- 合并两个链表:
- 使用哑节点
dummy
简化链表头处理 - 双指针遍历两个链表,比较节点值,将较小节点接入新链表
- 当一个链表遍历完时,将另一链表剩余部分直接接入
代码实现(Java版)
class Solution {public ListNode mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0) return null;if (n == 1) return lists[0];while (n > 1) {int newN = (n + 1) / 2;for (int i = 0; i < n / 2; i++) {lists[i] = mergeTwoLists(lists[2 * i], lists[2 * i + 1]);}if (n % 2 == 1) {lists[newN - 1] = lists[n - 1];}n = newN;}return lists[0];}private ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode();ListNode cur = dummy;while (l1 != null && l2 != null) {if (l1.val <= l2.val) {cur.next = l1;l1 = l1.next;} else {cur.next = l2;l2 = l2.next;}cur = cur.next;}cur.next = (l1 != null) ? l1 : l2;return dummy.next;}
}
代码说明
- 主方法
mergeKLists
:
- 边界处理:直接处理空数组或单链表情况
- 分治合并:
n
表示当前待合并的链表数量newN = (n + 1) / 2
计算合并后链表数量- 循环合并相邻链表:
lists[i] = mergeTwoLists(lists[2*i], lists[2*i+1])
- 处理奇数链表:将最后一个链表移至
newN-1
位置 - 更新
n = newN
进行下一轮合并
- 返回
lists[0]
:最终合并结果
- 辅助方法
mergeTwoLists
:
- 哑节点:
dummy
统一处理头节点 - 双指针遍历:
- 比较
l1
和l2
节点值,较小者接入新链表 - 移动对应链表指针及新链表指针
cur
- 比较
- 剩余链表处理:循环结束后将非空链表剩余部分直接接入