题目:
https://leetcode.com/problems/merge-k-sorted-lists/
五种方法
- 蛮力算法,两个合并,得到的结果再和另外一个合并,直到最后。
复杂度为O(N*K^2)
/*------------------------------------------------------------------*/
class Solution1 {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {//两个两个比较,直到最后合成一个(这个方法超时,AC不了)复杂度O(N*K^2)
ListNode* result=NULL;
if(lists.size()==0) return result;
else if(lists.size()==1) return lists[0];
else
{
for(int i=0;i<lists.size();i++)
{
if(i==0)
{
result=merge2Lists(lists[0],lists[1]);
}
else if(i>=2)
{
result=merge2Lists(lists[i],result);
}
}
return result;
}
}
private:
ListNode *merge2Lists(ListNode*l1,ListNode*l2 )
{
if(l1==NULL) return l2;
else if(l2==NULL) return l1;
else
{
ListNode *res=NULL;
if(l1->val<l2->val)
{
res=l1;
res->next=merge2Lists(l1->next,l2);
}
else
{
res=l2;
res->next=merge2Lists(l1,l2->next);
}
return res;
}
}
};
2.
分治法(两个两个合并,第一次合并结束,是k/2个序列,再两个两个合并,第二次合并结束是 k/4,最后的复杂度是O(N*log(K)))
/*------------------------------------------------------------------*/
class Solution2 {//复杂度:O(N*log(K)) 空间复杂度大
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {//分治法,一半一半比较,直到最后合成一个(这个方法AC了,不会存在空间超出限制,70%排名)
ListNode* result=NULL;
if(lists.size()==0) return result;
else if(lists.size()==1) return lists[0];
return merg(lists,0,lists.size()-1);
}
private:
ListNode *merg(vector<ListNode*>& lists,int left,int right)
{
if(left<right)
{
int mid=(right+left)/2;
return merge2Lists(merg(lists,left,mid),merg(lists,mid+1,right));
}
return lists[left];//返回左边那个,其实这时左边等于右边。return[right]效果一样
}
ListNode *merge2Lists(ListNode*l1,ListNode*l2 )
{
if(l1==NULL) return l2;
else if(l2==NULL) return l1;
else
{
ListNode *res=NULL;
if(l1->val<l2->val)
{
res=l1;
res->next=merge2Lists(l1->next,l2);
}
else
{
res=l2;
res->next=merge2Lists(l1,l2->next);
}
return res;
}
}
};
3
分治法,不采用递归,复杂度同2,但空间复杂度降低了为O(1)
/*------------------------------------------------------------------*/
//分治法,不采用递归,把第二种递归方法转化为非递归方法
class Solution3 {//复杂度:O(N*log(K)) 空间复杂度小
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {//分治法,AC,89%)
ListNode* result=NULL;
if(lists.size()==0) return result;
else if(lists.size()==1) return lists[0];
else
{
int len=lists.size();
while(true)
{
for(int i=0;i<=len/2;i++)
{
if(len%2==0&&i<len/2)
{
lists[i]=merge2Lists(lists[i],lists[len-i-1]);
}
else if(len%2==1)
{
if(i<len/2) lists[i]=merge2Lists(lists[i],lists[len-i-1]);
else lists[i]=lists[i];
}
}
if(len%2==0) len=len/2;
else len=len/2+1;
if(len==1)
break;
}
return lists[0];
}
}
ListNode *merge2Lists(ListNode*l1,ListNode*l2 )
{
if(l1==NULL) return l2;
else if(l2==NULL) return l1;
else
{
ListNode *res=NULL;
if(l1->val<l2->val)
{
res=l1;
res->next=merge2Lists(l1->next,l2);
}
else
{
res=l2;
res->next=merge2Lists(l1,l2->next);
}
return res;
}
}
};
4
使用STL的优先队列,即小顶堆的性质。复杂度:O(NK*log(K)) 堆排序
/*------------------------------------------------------------------*/
//优先队列(其实就是大顶堆排序),使用STL的自动建堆
class Solution4 {//复杂度:O(NK*log(K)) 堆排序
public:
struct cmp{
bool operator()(ListNode *a,ListNode *b){//大的在下,小的在上
return a->val>b->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* result=NULL;
if(lists.size()==0) return result;
else if(lists.size()==1) return lists[0];
priority_queue<ListNode *,vector<ListNode*>,cmp> queue;
for(int i=0;i<lists.size();i++)
{
if(lists[i]!=NULL)//对于非空的,加入堆中
queue.push(lists[i]);//建立堆
}
ListNode *head=NULL,*prev=NULL,*temp;
while(!queue.empty()){
temp=queue.top();//获取堆顶元素,也就是等效于获取当前所有链表中最小的那个元素
queue.pop();//弹出顶部元素
if(prev==NULL)
{
prev=temp;
head=prev;//第一个元素给head,即用head指向第一个元素
}
else
{
prev->next=temp;//以后的元素都赋给prev->next
prev=prev->next;//prev指向它的下一个元素
}
if(temp->next!=NULL)//也就是说temp所在的链表还有元素(temp后面的),就把temp后面的元素加入堆中
{
queue.push(temp->next);
}
}
return head;//返回指向第一个元素的指针,也就是返回已经排好序的一大串
}
};
5
自己写的小顶堆,不适用STL的。复杂度同4.
/*------------------------------------------------------------------*/
//自己建立堆
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists){
ListNode* result=NULL;
if(lists.size()==0) return result;
else if(lists.size()==1) return lists[0];
vector<ListNode *>heap;
for(int i=0;i<lists.size();i++)
if(lists[i]) heap.push_back(lists[i]);
makeheap(heap);//建堆函数,自己写
ListNode *head=NULL,*prev=NULL,*temp;
while(!heap.empty())
{
temp=heap_pop(heap);//弹出堆顶元素,并返回,自己写这个函数
if(prev==NULL)
{
prev=temp;
head=prev;
}
else
{
prev->next=temp;
prev=prev->next;
}
if(temp->next!=NULL)
{
heap_push(heap,temp->next);//加入新元素,自己写这个函数
}
}
return head;
}
private:
void makeheap(vector<ListNode *> &heap)
{
for(int i=heap.size()/2;i>0;i--)//堆的序号是从1到heap.size()/2
minHeap(heap,i);
}
void minHeap(vector<ListNode *> &heap,int i)//以i为父节点的子树,建立堆
{
int lchild=2*i;//i的左孩子节点序号(序号是从1开始)
int rchild=2*i+1;//i的右孩子节点序号
int least=i;//临时变量
if(lchild<=heap.size()&&heap[lchild-1]->val<heap[least-1]->val)
{
least=lchild;
}
if(rchild<=heap.size()&&heap[rchild-1]->val<heap[least-1]->val)
{
least=rchild;
}
//上述两个if是为了获取i节点,i节点的左右孩子三个中最小的那个,下标存到least
if(least!=i)//不是父节点i
{
swap(heap[i-1],heap[least-1]);
minHeap(heap,least);//避免调整之后以least为父节点的子树不是堆(因为此时左右子树的一个和父节点换了,那么换后的左右子树那个还要继续调整)
}
}
void swap(ListNode *&a,ListNode *&b)//指针作为形参传递,加引用才能在调用时更改其值!(这儿困惑了好久,一直不明白)
{
ListNode * c;
c=a;
a=b;
b=c;
}
void heap_push(vector<ListNode*> &heap,ListNode * p)
{
heap.push_back(p);//把该链表加入heap中
//minHeap(heap, 1);
for(int child=heap.size(),parent=child/2;parent>0;child=child/2,parent=child/2) //因为添加的元素是最后一个,所以只需从底部向上进行判断
{
if(heap[child-1]->val<heap[parent-1]->val)
swap(heap[child-1],heap[parent-1]);
}
}
ListNode*heap_pop(vector<ListNode *>&heap)//删除堆顶的元素,并返回
{
swap(heap[0],heap[heap.size()-1]);//把最后一个和第一个交换,是为了方便删除第一个
ListNode* p=heap[heap.size()-1];//获取最后一个
heap.pop_back();
minHeap(heap,1);//重新建立堆(第一步交换后,要重新调整顺序),因为现在的第一个是原来的最后一个,所以要从头到尾堆排序一下
return p;
}
};
总结:2,3,4,5都AC了,1 TLE。另外自己写小顶堆,错误很多,好多没考虑清楚的,调试了好久,不过写一次确实能锻炼能力。