合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
这题可以考虑用二路归并排序,也就是分治法,两两一组合并,最终得到有序的链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *merge(ListNode *p1,ListNode *p2){
ListNode dummy(-1),*root=&dummy;
while(p1!=NULL&&p2!=NULL){
if(p1->val<p2->val){
root->next=p1;
p1=p1->next;
}
else{
root->next=p2;
p2=p2->next;
}
root=root->next;
}
if(p1==NULL) root->next=p2;
else root->next=p1;
return dummy.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
vector<ListNode*> tmp;
int n=lists.size();
while(n>1){
int i=0,j=0;
for(i=0,j=0;i<n-1;i+=2){
ListNode* t=merge(lists[i],lists[i+1]);
if(t!=NULL)
lists[j++]=t;
}
if(i==n-1) lists[j++]=lists[i];
n=j;
}
return lists.empty()?NULL:lists[0];
}
};
分治法需要多次merge
的过程,每个链表既然已经有序了,其实每次只需用从k
个链表的头结点中选出最小的即可,那么可以考虑把k
个链表的头结点放入优先级队列中(优先级队列一般是大顶堆,这里要做一下处理,变成小顶堆),然后每次取出top
即为最小,在top
对应节点的下一个节点放入优先级队列中即可。优先级队列插入的时间复杂度是o(logn)
,而取出top
的时间复杂度是o(1)
,所以可以较大的提高效率
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class cmp{
public:
bool operator()(const ListNode* p1,const ListNode* p2){
return p1->val>p2->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode dummy(-1),*root=&dummy;
priority_queue<ListNode*,vector<ListNode*>,cmp> q;
int n=lists.size();
for(int i=0;i<n;i++){
if(lists[i]==NULL) continue;
q.push(lists[i]);
}
while(!q.empty()){
ListNode* node=q.top();
q.pop();
root->next=node;
root=root->next;
if(node->next!=NULL) q.push(node->next);
root->next=NULL;
}
return dummy.next;
}
};