zoukankan      html  css  js  c++  java
  • LeetCode(C++)刷题计划:23-合并K个排序链表

    23-合并K个排序链表

    @Author:CSU张扬
    @Email:csuzhangyang@gmail.com or csuzhangyang@qq.com

    Category Difficulty Pass rate Tags Companies
    algorithms Medium 72.20% linked-list / divide-and-conquer / heap airbnb / amazon / facebook / google / linkedin / microsoft / twitter / uber

    1. 题目

    合并 kk 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

    示例:
    输入:
    [
      1->4->5,
      1->3->4,
      2->6
    ]
    输出: 1->1->2->3->4->4->5->6
    

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/merge-k-sorted-lists

    2. 解法

    2.1 解法一:全部合并再排序

    暴力而有效的解法:
    我们将 kk 个链表全部放入向量中,然后进行排序,最后再放回到一个链表中。

    当然我们也可以使用优先队列,那么在放入元素时自动进行了排序。

    两者的性能是几乎没有任何差别的。

    2.1.1 向量 vector

    执行用时: 32 ms, 在所有 cpp 提交中击败了89.24%的用户
    内存消耗: 11.7 MB, 在所有 cpp 提交中击败了63.01%的用户

    /**
     * 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) {
            vector<int> elem;
            ListNode *head = new ListNode(0);
            ListNode *h = head;
            for (auto &vec : lists) {
                while(vec) {
                    elem.push_back(vec->val);
                    vec = vec->next;
                }
            }
            sort(elem.begin(), elem.end());
            for (auto i : elem) {
                head->next = new ListNode(i);
                head = head->next;
            }
            ListNode *ptrDelete = h;
            h = h->next;
            delete ptrDelete;
            return h;
        }
    };
    

    2.1.2 优先队列 priority_queue

    执行用时: 36 ms, 在所有 cpp 提交中击败了78.17%的用户
    内存消耗: 11.6 MB, 在所有 cpp 提交中击败了66.99%的用户

    class Solution {
    public:
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            priority_queue<int, vector<int>, greater<int>> q;
            ListNode *head = new ListNode(0);
            ListNode *h = head;
            for (auto &vec : lists) {
                while(vec) {
                    q.push(vec->val);
                    vec = vec->next;
                }
            }
            while (!q.empty()) {
                head->next = new ListNode(q.top());
                q.pop();
                head = head->next;
            }
            ListNode *ptrDelete = h;
            h = h->next;
            delete ptrDelete;
            return h;
        }
    };
    

    2.2 解法二:两两合并链表

    利用 LeetCode-21题:合并两个有序链表 ,将合并 kk 个链表转化为合并 k1k-1 次两个链表。

    也就是将前两个链表合并成新的链表,新链表再和第三个链表合并,合并出的新链表再和第四个链表合并…
    不过这个方法的复杂度很高。

    执行用时: 1428 ms, 在所有 cpp 提交中击败了5.02%的用户
    内存消耗: 13.9 MB, 在所有 cpp 提交中击败了17.09%的用户

    class Solution {
    public:
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            ListNode *res = nullptr;
            for (auto vec : lists) {
                res = mergeTwoLists(res, vec);
            }
            return res;
        }
        ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
            if (!a || b && a->val > b->val) swap(a, b);
            if (a) a->next = mergeTwoLists(a->next, b);
            return a;
        }
    };
    

    2.3 解法三:分治

    参考 Sun

    1. 两两合并链表,最后变成了 k2frac{k}{2} 个链表,继续合并,链表数目 k2frac{k}{2} -> k4frac{k}{4} -> k8frac{k}{8}… ,直至最后变为一个链表。
    2. 我们使用队列实现该操作,将队列前两个链表合并后并弹出,合并后的链表添加到队列尾部,如此循环,直至队列中只有一个链表。

    执行用时: 28 ms, 在所有 cpp 提交中击败了96.42%的用户
    内存消耗: 14.2 MB, 在所有 cpp 提交中击败了17.09%的用户

    class Solution {
    public:
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            if (lists.size() == 0)
                return NULL;
            if (lists.size() == 1)
                return lists[0];
            queue<ListNode*> q;
            for (auto vec : lists)
                q.push(vec);
            while(q.size() > 1) {
                ListNode* l1 = q.front();
                q.pop();
                ListNode* l2 = q.front();
                q.pop();
                q.push(mergeTwoLists(l1, l2));
            }
            return q.front();
        }
        ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
            if (!a || b && a->val > b->val) swap(a, b);
            if (a) a->next = mergeTwoLists(a->next, b);
            return a;
        }
    };
    
  • 相关阅读:
    [Linux 004]——用户和用户组以及 Linux 权限管理(二)
    [Linux 003]——用户和用户组以及 Linux 权限管理(一)
    [Linux 002]——Linux的常用命令
    [Linux 001]——计算机和操作系统的基础知识
    给 Android 开发者的 RxJava 详解
    Mac OSX系统搭建React natvie for android 开发环境
    Java中的堆和栈的区别
    Nginx配置详解
    在博客园安家了!
    J2SE核心实战开发—— 集合类框架
  • 原文地址:https://www.cnblogs.com/MagicConch/p/12179147.html
Copyright © 2011-2022 走看看