问题
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
例子

思路
-
方法1
-


遍历链表数组,顺序取出链表和head合并
-
方法2
-

-


归并分治的方法,一次合并2i和2i+1,并放在i下标【如果为奇数lists[len/2]=lists[len-1]】最后返回lists[0]
-
方法 总共n个元素,k个链
** 逐一合并**
2nk+3nk+..+knk=nk×k22=O(nk)2\frac{n}{k}+3\frac{n}{k}+..+k\frac{n}{k}=\frac{n}{k}\times\frac{k^2}{2}=O(nk)2kn+3kn+..+kkn=kn×2k2=O(nk)
两两合并
第一次合并:2nk∗k2=n第一次合并:2\frac{n}{k}*\frac{k}{2}=n第一次合并:2kn∗2k=n
第二次合并:22nk∗k4=n第二次合并:2^2\frac{n}{k}*\frac{k}{4}=n第二次合并:22kn∗4k=n
第logk次合并:2logknk∗kk=n第logk次合并:2^{logk}\frac{n}{k}*\frac{k}{k}=n第logk次合并:2logkkn∗kk=n
时间复杂度:O(nlogk)时间复杂度:O(nlogk)时间复杂度:O(nlogk)
优先队列,最小堆
遍历n次,每次调整时间logk−>O(nlogk)遍历n次,每次调整时间logk->O(nlogk)遍历n次,每次调整时间logk−>O(nlogk)
代码 -
合并K个排序链表
//方法1 逐一合并 O(nk)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
// ListNode head=null;
// for(ListNode node:lists) {
// head=merge(head,node);
// }
// return head;
}
public ListNode merge(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
if(l1.val<l2.val) {
l1.next=merge(l1.next,l2);
return l1;
}
l2.next=merge(l1,l2.next);
return l2;
}
}
//方法2 两两合并O(nlogk)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int len=lists.length;
if(len==0) return null;
while(len>1) {
for(int i=0; i<len/2; i++) {
lists[i]=merge(lists[2*i],lists[2*i+1]);
}
//如果lists总数是奇数个,则还剩一个
if(len%2==1) {
lists[len/2]=lists[len-1];
len=len/2+1;//规模减半+1
}else len=len/2;//规模减半
}
return lists[0];
}
public ListNode merge(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
if(l1.val<l2.val) {
l1.next=merge(l1.next,l2);
return l1;
}
l2.next=merge(l1,l2.next);
return l2;
}
}
//优先队列,小顶堆 O(nlogk)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length==0) return null;
int k = lists.length;
PriorityQueue<ListNode> q = new PriorityQueue<>((l1,l2)->l1.val-l2.val);
for(int i=0; i<k; i++) {
if(lists[i]==null) continue;//因为没有说每个链表的长度
q.offer(lists[i]);
}
ListNode h = new ListNode(-1),cur=h;
while(q.size()>0){
ListNode node = q.poll();
cur.next = node;
cur=cur.next;
if(node.next!=null) {
q.offer(node.next);
}
}
return h.next;
}
- 合并k个数组,总元素为n个
//暴力,先合并,再排序 O(nlogn)
//逐一合并 O(nk)
//两两合并O(nlogk)
//不能使用优先队列,小顶堆,因为无法根据优先队列中的最小值获取其所在的数组及对应的下一个元素

本文探讨了如何合并k个已排序的链表,提供了三种方法:顺序合并、分治合并和使用优先队列(最小堆)。详细分析了每种方法的时间复杂度,其中分治合并和最小堆方法具有较好的效率,分别为O(nlogk)。
252

被折叠的 条评论
为什么被折叠?



