给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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
思路:数组二分,递归归并
这道题目可以用分治法来解决,具体来说,我们可以将链表数组分成两个子数组,分别递归地将它们合并成一个有序链表,然后再将这两个有序链表合并成一个有序链表。 为了方便起见,我们可以将链表数组的下标范围 定义为当前要合并的区间,然后递归地将它分成两个子区间 和 ,其中 。然后递归地将两个子区间合并成一个有序链表,并返回合并后的链表头指针。 合并两个有序链表的过程可以使用迭代或递归来实现。这里我们使用迭代的方式来实现,具体来说,我们可以定义一个虚拟头节点 dummy,然后遍历两个链表,将它们的节点按照升序依次添加到 dummy 的后面,最后返回 dummy 的下一个节点即可。
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
const mergeTwoLists = (l: ListNode | null, r: ListNode | null) => {
const dummy = new ListNode();
let p: ListNode | null = dummy;
while(l && r) {
if(l.val < r.val) {
p.next = l;
l = l.next
} else {
p.next = r;
r = r.next
}
p = p.next;
}
p.next = l ? l : r;
return dummy.next;
}
const merge = (lists: Array<ListNode | null>, start: number, end: number) => {
if(start === end) return lists[start];// 奇数的中间
const mid = Math.floor((end + start) / 2);
const left = merge(lists, start, mid);
const right = merge(lists, mid + 1, end);
return mergeTwoLists(left, right)
}
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
if(!lists || !lists.length) return null;
return merge(lists, 0, lists.length - 1)
};
总结
- 时间复杂度 假设链表数组的长度为 ,每个链表的平均长度为 。在递归过程中,每个链表节点最多被访问一次,因此时间复杂度为 。
- 空间复杂度为 ,即递归栈的深度。