原题地址(困难)
此题虽是困难,但是做法超级多啊,而且也有很容易想到的做法。
方法1—顺序合并
思路
这是我最想想到的做法,很简单。
两个链表合并我们都会,多个链表呢?用个循环呗。就这么简单。
代码
class Solution {public:ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.size() == 0) return {};ListNode* dummy = new ListNode(0); //虚头结点ListNode* p, *q, *pre, *tmp;dummy->next = lists[0];for(int i=1; i<lists.size(); i++) {// 把第i个链表和ret合并在一起pre = dummy;p = dummy->next;q = lists[i];while(q && p) {if(q->val <= p->val) {tmp = q;q = q->next;pre->next = tmp;tmp->next = p;}else{p = p->next;}pre = pre->next;}if(!p && q) {pre->next = q;}}return dummy->next;}};
时空复杂度
时间复杂度
假设链表最长长度为,共有
个链表,那么时间复杂度为
因为有个链表,第一次合并,是合并第一、第二个链表;第二次合并,实质是第一、二个链表合并后的链表,再和第三个链表合并,因此所耗费时间为
,所以时间复杂度为
空间复杂度
因为是原地修改,所以空间复杂度为,如果不是原地修改的话,空间复杂度就为
方法2—分治合并
思路
可以用分治的思路对方法一进行优化,从而使时间复杂度降低。
盗用官方图:
代码
class Solution {public:ListNode* mergeTwoLists(ListNode* a, ListNode* b) {if(!a || !b) return a ? a : b;ListNode *dummy = new ListNode(0);ListNode *p = a, *q = b, *tail = dummy;// 尾插法while(p && q) {if(p->val <= q->val) {tail->next = p;p = p->next;}else{tail->next = q;q = q->next;}tail = tail->next;}tail->next = p ? p : q;return dummy->next;}ListNode* merge(vector<ListNode*> lists, int l, int r){if(l == r) return lists[l];if(l > r) return nullptr;int mid = (l + r) / 2;ListNode* a = merge(lists, l, mid);ListNode* b = merge(lists, mid+1, r);return mergeTwoLists(a, b);}ListNode* mergeKLists(vector<ListNode*>& lists) {int n = lists.size();if(n == 0) return nullptr;return merge(lists, 0, n - 1);}};
时空复杂度
时间复杂度
还是假设链表最长长度为,共有
个链表。
第一次合并,共合并次,每次耗费时间2n;
第二次合并,共合并次,每次耗费时间4n;
第i次合并,共合并 次,每次耗费时间
;
所以总耗费时间为,所以时间复杂度为
空间复杂度
方法3—优先队列
思路
这可能是这道题的最优方法,也是出题人所希望看到的方法。
那就是维护一个优先队列,每次弹出所有链表中最小值的那个节点,再用这个链表的下一个节点补上,直至所有链表都被弹完。
代码
class Solution {public:struct Status{int val;ListNode* node;bool operator < (const Status& s) const {return val > s.val;}};ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<Status> q;for(auto list : lists) {if(list) q.push({list->val, list});}ListNode head, *tail = &head;while(!q.empty()) {auto tmp = q.top();q.pop();tail->next = tmp.node;tail = tail->next;if(tmp.node->next) q.push({tmp.node->next->val, tmp.node->next});}return head.next;}};
时空复杂度
时间复杂度
长度为k的优先队列插入删除元素耗费时间都是,k个链表元素总数量不超过
,所以时间复杂度为
