题目:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
思路:
可以认为是Leetcode 21 Merge Two Sorted List的推广,其思路还是大同小异的,即每次从k个链表中找出头结点最小的那一个,加入到合并后的单链表末尾。假设k个列表中的节点个数总和 为m,那么朴素解法的时间复杂度为O(m*k)。但实际上在这里我们没有利用k个链表中的头结点的大小的相对稳定性,即每次当我们取出一个最小的头结点之后,其余头结点之间的相对大小还是稳定的,我们只需要更新新加入的头结点(如果有的话)到合适的位置,即可在O(1)的时间获得下一个最小的头结点。
事实上,有一类数据结构非常适这类应用场景:堆!堆的构建时间复杂度是O(k),取得最大最小值的时间复杂度为O(1),添加和删除元素的时间复杂度均为O(logk),简直就是为本题目量身定做的!在STL中,采用vector实现的heap以及priority_queue均可实现这一目标。这样,该算法的时间复杂度就降为O(m*logk),空间复杂度为O(k)。
代码:
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) {
vector<ListNode*> vec;
for(int i = 0; i < lists.size(); ++i)
{
if(lists[i] != NULL)
vec.push_back(lists[i]);
}
ListNode pre_head(0);
ListNode* node = &pre_head;
make_heap(vec.begin(), vec.end(), ListNodeComp);
while(vec.size() > 0)
{
ListNode* front = vec[0];
pop_heap(vec.begin(), vec.end(), ListNodeComp);
vec.pop_back();
node->next = front;
node = node->next;
if(front->next != NULL)
{
vec.push_back(front->next);
push_heap(vec.begin(), vec.end(), ListNodeComp);
}
}
return pre_head.next;
}
private:
struct ListNodeCompare
{
bool operator() (ListNode* l1, ListNode* l2) const
{
return l1->val > l2->val;
}
} ListNodeComp;
};
2、优先级队列版本:
/**
* 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) {
auto cmp = [](ListNode* a, ListNode* b) { return a->val > b->val; };
priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> que(cmp);
ListNode pre_head(0), *p = &pre_head;
for(auto val: lists)
if(val) que.push(val);
while(!que.empty())
{
auto val = que.top();
p->next = val, p = p->next;
que.pop();
if(val->next)
que.push(val->next);
}
return pre_head.next;
}
};