Description
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
实现链表版的 k 路归并并分析其复杂度。
思路
一看到归并且要求时间复杂度尽可能低,那么自然联想到 T(n) = O(nlgn) 的归并排序或堆排序,但是代码都比较长些,短时间内不易解决。有没有什么简单快捷地办法呢?有的,那就是利用最小堆实现 k 路归并。先把 k 条链的元素全部入堆,每次取出堆顶元素,去掉原先的链,再重新链成一串即可。
由于STL已经帮我们实现了优先队列,所以做题时直接使用 priority_queue 即可,它的模板声明带有三个参数, priority_queue<Type, Container, Functional> 其中 Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。主要方法有五个:push(), top(), pop(), size(), empty(),已经满足了本题需要的操作,入堆,取堆顶元素并出堆,查看当前堆的大小,堆是否为空。
此外还需要定义优先级,第一种方法是重载 () ,如下面的第一个例子。此时的 cmp 叫做仿函数/函数对象(functor),是指重载了operator()运算符的类,它广泛用于泛型编程。函数的形参形式最好写成 “const Type& a” 的形式,并且将该成员函数用 const 修饰。类似如下形式:
struct cmp{ bool operator () (const Type& a, const Type& b) const { return a.dat > b.dat; } };
priority_queue<Type, Container, cmp> pQueue;
或者,在类中重载 < 操作符,并重载为友元函数
struct T { Data dat; friend bool operator < (const Type& a, const Type& b) { return a.dat > b.dat; } }; priority_queue<T> pQueue;
扯远了,本题算法如下,由于每个元素入堆的时间是 O(lgn) ,需要线性扫描入堆也就是 O(nlgn),出堆时间是 O(lgn) ,串成链的时间是Θ(1),需要线性地让全部元素出堆也就是 O(nlgn),所以总时间 T(n) = O(nlgn) + O(nlgn) + n * Θ(1) = O(nlgn)
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ #include <iostream> #include <algorithm> struct cmp { bool operator() (const ListNode* a, const ListNode* b) const { return (a->val > b->val); } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue <ListNode*, vector<ListNode*>, cmp> pQueue; ListNode* lnode; for (auto c : lists) { lnode = c; while (lnode) { pQueue.push (lnode); lnode = lnode->next; } } if (pQueue.empty()) return NULL; ListNode *head, *p, *q; head = p = pQueue.top(); pQueue.pop(); head->next = p->next = NULL; //initialize first node of new linklist while (!pQueue.empty()) { q = pQueue.top(); pQueue.pop(); q->next = NULL; //broken pointer which was set previously p->next = q; p = q; } return head; } };