方法一:分治 + 递归
解题思路
在21. 合并两个有序链表,我们知道如何合并两个有序链表。而本题是合并 k
个有序链表,可以通过大问题拆分成小问题解决,即把 k
个链表,拆分成 k/2
个链表组,俩俩合并,直到合并成一个链表,这就是分而治之的思想。
可以通过递归来实现分治算法:
- 定义函数功能:合并链表数组中的
left
到right
个链表 - 结束条件:
left == right
,即只剩下一个链表,无须合并,直接返回 - 递推公式:将
[left, right]
拆分成两半,分别获得左边已合并的链表l1
和右边已合并的链表l2
,然后再对这两个链表进行合并。
代码
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public class Solution {
public ListNode MergeKLists(ListNode[] lists) {
if(lists == null || lists.Length == 0) {
return null;
}
return MergeKLists(lists, 0, lists.Length-1);
}
private ListNode MergeKLists(ListNode[] lists, int left, int right) {
if(left == right) {
return lists[left];
}
int mid = left + (right-left)/2;
ListNode l1 = MergeKLists(lists, left, mid);
ListNode l2 = MergeKLists(lists, mid+1, right);
return MergeTwoLists(l1, l2);
}
private ListNode MergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}
ListNode dummy = new ListNode();
ListNode p1 = l1, p2 = l2, cur = dummy;
while(p1 != null && p2 != null) {
if(p1.val < p2.val) {
cur.next = p1;
cur = cur.next;
p1 = p1.next;
} else {
cur.next = p2;
cur = cur.next;
p2 = p2.next;
}
}
cur.next = p1 == null ? p2 : p1;
return dummy.next;
}
}
复杂度分析
- 时间复杂度:(O(n*k*logk)),其中链表数组长度为 (k),链表的平均长度为 (n)。函数
MergeKLists
的时间复杂度为 (O(klogk))(分治),函数MergeTwoLists
的时间复杂度是 (O(n)),因此总的时间复杂度为 (O(n*k*logk))。 - 空间复杂度:(O(logk)),递归会使用到 (O(logk)) 空间代价的栈空间。