第一种方法:分治法。
思路:有点类似于MergeSort的思路,就是分治法,不了解MergeSort的朋友,请参见归并排序-维基百科,是一个比较经典的O(nlogn)的排序算法,还是比较重要的。思路是先分成两个子任务,然后递归求子任务,最后回溯回来。这个题目也是这样,先把k个list分成两半,然后继续划分,直到剩下两个list就合并起来,合并时会用到Merge Two Sorted Lists这道题,不熟悉的朋友可以复习一下。
我们来分析一下上述算法的时间复杂度。假设总共有k个list,每个list的最大长度是n,那么运行时间满足递推式T(k) = 2T(k/2)+O(n*k)。根据主定理,可以算出算法的总复杂度是O(nklogk)。如果不了解主定理的朋友,可以参见主定理-维基百科。空间复杂度的话是递归栈的大小O(logk)。
代码:
/**
* Definition for singly-linked list.* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge2(ListNode* left_p,ListNode* right_p){
if(left_p==NULL) return right_p;
if(right_p==NULL) return left_p;
ListNode* result;
ListNode* cur_p;
if(left_p->val<=right_p->val){
result=left_p;
cur_p=left_p;
left_p=left_p->next;
}
else{
result=right_p;
cur_p=right_p;
right_p=right_p->next;
}
while(left_p!=NULL && right_p!=NULL){
if(left_p->val<=right_p->val){
cur_p->next=left_p;
cur_p=left_p;
left_p=left_p->next;
}else{
cur_p->next=right_p;
cur_p=right_p;
right_p=right_p->next;
}
}
if(left_p==NULL)
cur_p->next=right_p;
else
cur_p->next=left_p;
return result;
}
ListNode* devide(vector<ListNode*>& lists,int left,int right){
if(left>=right) return lists[left];
int mid=(left+right)/2;
return merge2(devide(lists,left,mid),devide(lists,mid+1,right));
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len=lists.size();
if(len<=0) return NULL;
if(len==1) return lists[0];
ListNode* result=devide(lists,0,len-1);
return result;
}
};
第二种方法:堆。
维护一个大小为k的堆,每次取堆顶的最小元素放到结果中,然后读取该元素的下一个元素放入堆中,重新维护好。因为每个链表是有序的,每次又是去当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是O(nklogk)。空间复杂度是堆的大小,即为O(k)。代码如下:
堆用了STL里面的堆函数:
//比较函数,注意这里最小堆是用大于函数,比较奇怪。
bool cmp(ListNode* node1,ListNode* node2){
if(node1->val>=node2->val){
return true;
}else{
return false;
}
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len=lists.size();
//为了使用STL的堆函数,得先开辟一个数组来保存堆元素。
ListNode** min_heap=new ListNode*[len+1];
int size=0;
for(int i=0;i<len;++i){
if(lists[i]!=NULL){
min_heap[size++]=lists[i];
}
}
if(size==0){
delete []min_heap;
return NULL;
}
if(size==1){
ListNode* result=min_heap[0];
delete []min_heap;
return result;
}
make_heap(&min_heap[0],&min_heap[size],cmp);
ListNode *new_lists,*result;
new_lists=min_heap[0];
result=new_lists;
//取出第一个节点
if(min_heap[0]->next!=NULL){
min_heap[0]=min_heap[0]->next;
make_heap(&min_heap[0],&min_heap[size],cmp);
}else{
pop_heap(&min_heap[0],&min_heap[size],cmp);
size--;
}
//开始入堆出堆的过程
while(size>0){
new_lists->next=min_heap[0];
new_lists=new_lists->next;
if(min_heap[0]->next==NULL){
pop_heap(&min_heap[0],&min_heap[size],cmp);
size--;
}else{
min_heap[0]=min_heap[0]->next;
make_heap(&min_heap[0],&min_heap[size],cmp);
/*上面两句也可以用下面三句代替
pop_heap(&min_heap[0],&min_heap[size],cmp);
min_heap[size-1]=min_heap[size-1]->next;
push_heap(&min_heap[0],&min_heap[size],cmp);
*/
}
}
delete []min_heap;
return result;
}
};