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. 两两合并链表,最后变成了 k2\frac{k}{2} 个链表,继续合并,链表数目 k2\frac{k}{2} -> k4\frac{k}{4} -> k8\frac{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;
    }
};

posted on 2020-01-11 10:23  神奇小海螺  阅读(298)  评论(0编辑  收藏  举报

导航