题意:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. (Hard)
分析:
方法1:
利用做过的merge 2 sorted list,将头两个归并,结果再与下一个归并,以此类推,归并完所有。
时间复杂度分析是个小问题, merge 2 sorted list的复杂度是O(n),本以为结果就是O(n * k)。
但是仔细考虑,随着归并不断进行,其中一个链表的长度不断增加,直至O(n * k),所以复杂度应该是O(n * k^2)
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
private:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode dummy();
ListNode* head = &dummy;
while (l1 != nullptr && l2 != nullptr) {
if (l1 -> val < l2 -> val) {
head -> next = l1;
l1 = l1 -> next;
}
else {
head -> next = l2;
l2 = l2 -> next;
}
head = head -> next;
}
if (l1 != nullptr) {
head -> next = l1;
}
if (l2 != nullptr) {
head -> next = l2;
}
return dummy.next;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == ) {
return nullptr;
}
ListNode dummy();
ListNode* head = &dummy;
head -> next = lists[];
for (int i = ; i < lists.size(); ++i) {
head -> next = mergeTwoLists(head->next, lists[i]);
}
return dummy.next; }
};
方法2:
利用最小堆。将所有链表的第一个节点加入堆,然后每次取出值最小的节点,并将该节点的next节点加入堆中。堆为空后所以节点处理完,归并后链表。
堆的大小为k,所以插入删除取节点复杂度均为O(logk),共对O(nk)个节点进行操作,所以时间复杂度为O(nklogk)。
注:
本题除了算法思想本身,还有对于实现过程还是有需要总结的。
1) STL的priority_queue默认的是最大堆,需要重载比较函数。
首先一般来讲priority有三个参数,即
template < class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
可以看出,2、3有缺省值,即表示用vector实现priority_queue,实现的是最大堆。所以可以不写这两个参数。
但是如果要修改比较方式,即第三个参数的话,第二个参数也要给出(以前没用过,真是查文档才知道)。
对于内置类型,如int,可以使用stl的functor如 greater<int>直接修改堆的大小属性,但是自定义节点,如本例的ListNode,需要自己重载。
2)对于传入比较函数,一种方法是重载类的operator <,但是leetcode中ListNode的定义是不能修改的。
还有就是写一个cmp函数,值得注意的是。funtor作为一个类需要重载其operator(),即对于本题,写为
struct cmp{
bool operator() (ListNode* l1, ListNode* l2) {
return l1 -> val > l2 -> val;
}
};
另外,priority_queue的第三个参数不能只传函数,必须封装成个functor(类),开始写的时候传函数报错了。
总之这题收货还是不少,不论从算法角度还是语言角度。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
private:
struct cmp{
bool operator() (ListNode* l1, ListNode* l2) { //why?!
return l1 -> val > l2 -> val;
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode dummy();
ListNode* head = &dummy;
priority_queue<ListNode*, vector<ListNode*>, cmp> que;
for (int i = ; i < lists.size(); ++i) {
if (lists[i] != nullptr) {
que.push(lists[i]);
}
}
while (!que.empty()) {
ListNode* temp = que.top();
head -> next = temp;
head = head -> next;
que.pop();
if (temp -> next != nullptr) {
que.push(temp->next);
}
}
return dummy.next;
}
};