23.Merge K Sorted Lists

第一种方法:分治法。

思路:有点类似于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;
    }
};

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值