leetcode23. 合并K个排序链表 hard
题目描述:
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
解题思路:
两种方法的复杂度都是 O(nklogk)
方法1: 分治
不停的对半划分,比如k个链表先划分为合并两个k/2个链表的任务,再不停的往下划分,直到划分成只有一个或两个链表的任务,开始合并。举个例子来说比如合并6个链表,那么按照分治法,我们首先分别合并0和3,1和4,2和5。这样下一次只需合并3个链表,我们再合并1和3,最后和2合并就可以了。代码中的k是通过 (n+1)/2 计算的,这里为啥要加1呢,这是为了当n为奇数的时候,k能始终从后半段开始,比如当n=5时,那么此时k=3,则0和3合并,1和4合并,最中间的2空出来。当n是偶数的时候,加1也不会有影响,比如当n=4时,此时k=2,那么0和2合并,1和3合并,完美解决问题。
方法2:最小堆
用了最小堆这种数据结构,我们首先把k个链表的首元素都加入最小堆中,它们会自动排好序。然后我们每次取出最小的那个元素加入我们最终结果的链表中,然后把取出元素的下一个元素再加入堆中,下次仍从堆中取出最小的元素做相同的操作,以此类推,直到堆中没有元素了,此时k个链表也合并为了一个链表,返回首节点即可。
tips:priority_queue<ListNode*,vector<ListNode*>,decltype(cmp)> pq(cmp); //注意写法
方法1代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n=lists.size();
if(n==0)
return nullptr;
while(n>1){
int k=(n+1)/2; // 假如总数是奇数个 让中间这个不参与merge
for(int i=0;i<n/2;++i){
lists[i]=merge2lists(lists[i],lists[i+k]);
}
n=k;
} // 这一部分是精华
return lists[0];
}
ListNode * merge2lists(ListNode *head1,ListNode *head2){
if(!head1)
return head2;
if(!head2)
return head1;
ListNode dummy(0);
ListNode *cur=&dummy;
while(head1 && head2){
if(head1->val<head2->val){
cur->next=head1;
head1=head1->next;
}
else{
cur->next=head2;
head2=head2->next;
}
cur=cur->next;
}
if(head1)
cur->next=head1;
if(head2)
cur->next=head2;
return dummy.next;
}
};
方法2代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty())
return nullptr;
auto cmp=[]( ListNode *&a, ListNode *&b){
return a->val>b->val; //因为要小顶堆, false的优先级高
};
priority_queue<ListNode*,vector<ListNode*>,decltype(cmp)> pq(cmp); //注意写法
for(auto i:lists){
if(i!=nullptr)
pq.push(i);
}
ListNode dummy(0);
ListNode *pre=&dummy;
ListNode *cur=nullptr;
while(pq.size()){
cur=pq.top();
pq.pop();
pre->next=cur;
if(cur->next) pq.push(cur->next);
pre=pre->next;
}
return dummy.next;
}
};