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. 题目
合并 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
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 解法一:全部合并再排序
暴力而有效的解法:
我们将 个链表全部放入向量中,然后进行排序,最后再放回到一个链表中。
当然我们也可以使用优先队列,那么在放入元素时自动进行了排序。
两者的性能是几乎没有任何差别的。
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题:合并两个有序链表 ,将合并 个链表转化为合并 次两个链表。
也就是将前两个链表合并成新的链表,新链表再和第三个链表合并,合并出的新链表再和第四个链表合并…
不过这个方法的复杂度很高。
执行用时: 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
- 两两合并链表,最后变成了 个链表,继续合并,链表数目 -> -> … ,直至最后变为一个链表。
- 我们使用队列实现该操作,将队列前两个链表合并后并弹出,合并后的链表添加到队列尾部,如此循环,直至队列中只有一个链表。
执行用时: 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;
}
};