Leetcode-Merge k Sorted Lists

题目:
https://leetcode.com/problems/merge-k-sorted-lists/

五种方法

  1. 蛮力算法,两个合并,得到的结果再和另外一个合并,直到最后。
    复杂度为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。另外自己写小顶堆,错误很多,好多没考虑清楚的,调试了好久,不过写一次确实能锻炼能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值