zoukankan      html  css  js  c++  java
  • leetcode 23. Merge k Sorted Lists(堆||分治法)

    Merge k sorted linked lists and return it as one sorted list. 

    题意:把k个已经排好序的链表整合到一个链表中,并且这个链表是排了序的。

    题解:这是一道经典好题,值得仔细一说。

    有两种方法,假设每个链表的平均长度是n,那么这两种方法的时间复杂度都是O(nklogk)。

    方法一

    基本思路是:把k个链表开头的值排个序,每次取最小的一个值放到答案链表中,这次取完之后更新这个值为它后面的一个值。接着这么取一直到全部取完。那么每次更新之后怎么对当前这k个值重新排序以便知道当前最小的是谁呢?用优先队列(或者堆)来维护这k个值就好啦!

    由于每个值都要取一次,一共取nk次。每次更新优先队列要logk的复杂度。所以总时间复杂度为O(nklogk);空间复杂度为优先队列所占空间,为O(k)。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution
    {
    public:
        struct cmp
        {
            bool operator() (const ListNode* a,const ListNode* b)
            {
                return a->val  > b->val;
            }
        };
        ListNode* mergeKLists(vector<ListNode*>& lists)
        {
            priority_queue<ListNode*, vector<ListNode*>, cmp>pq;
            for(auto i:lists)
            {
                if(i) //这句判断很有必要,不能把空的加入队列。比如这组数据:[[],[]]
                {
                    pq.push(i);
                }
            }
            if(pq.empty())
            {
                return nullptr;
            }
            ListNode* ans = pq.top();
            pq.pop();
            ListNode* tail = ans;
            if(tail->next)
            {
                pq.push(tail->next);
            }
            while(!pq.empty())
            {
                tail->next = pq.top();
                tail = tail->next;
                pq.pop();
                if(tail->next)
                {
                    pq.push(tail->next);
                }
            }
            return ans;
        }
    };

    方法二:

    和方法一的思路不同:考虑分治的思想来解这个题(类似归并排序的思路)。把这些链表分成两半,如果每一半都合并好了,那么我就最后把这两个合并了就行了。这就是分治法的核心思想。

    但是这道题由于存的都是指针,就具有了更大的操作灵活性,可以不用递归来实现分治。就是先两两合并后在两两合并。。。一直下去直到最后成了一个。(相当于分治算法的那棵二叉树从底向上走了)。

    第一次两两合并是进行了k/2次,每次处理2n个值。

    第二次两两合并是进行了k/4次,每次处理4n个值。

    。。。

    最后一次两两合并是进行了k/(2^logk)次,每次处理2^logK*N个值。

    所以时间复杂度:

    O((2N) * (K / 2) + (4N) * (K / 4) + (8N) * (K / 8) + .............. + (2^logK*N) * (K / (2 ^logK)) )=O( logK*KN)

    空间复杂度是O(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) {
        if(lists.empty()){
            return nullptr;
        }
        while(lists.size() > 1){
            lists.push_back(mergeTwoLists(lists[0], lists[1]));
            lists.erase(lists.begin());
            lists.erase(lists.begin());
        }
        return lists.front();
        }
        ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
            if(l1 == nullptr){
                return l2;
            }
            if(l2 == nullptr){
                return l1;
            }
            if(l1->val <= l2->val){
                l1->next = mergeTwoLists(l1->next, l2);
                return l1;
            }
            else{
                l2->next = mergeTwoLists(l1, l2->next);
                return l2;
            }
        }
    };
  • 相关阅读:
    vim的一些基本配置
    做菜好吃的小技巧02
    SQLServer创建用户登录
    Python库整理
    centos7开机界面出现多个选项
    Elasticsearch和MongoDB简要对比
    OLTP与OLAP
    CentOS7命令总结
    windows下快速删除命令
    Idea配置热部署
  • 原文地址:https://www.cnblogs.com/zywscq/p/5403051.html
Copyright © 2011-2022 走看看