题目
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
分析
分治
将k个链表两两配对合并为一个更长的有序链表,将原问题规约为规模更小的子问题。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
class Solution { public: ListNode *mergeTwoLists(ListNode *h1, ListNode *h2) { if (h1 == nullptr || h2 == nullptr) return h1 ? h1 : h2; ListNode dummy, *tail = &dummy; while (h1 && h2) { if (h1->val <= h2->val) { tail->next = h1; h1 = h1->next; } else { tail->next = h2; h2 = h2->next; } tail = tail->next; } tail->next = h1 ? h1 : h2; 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 - l) >> 1); return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r)); } ListNode* mergeKLists(vector<ListNode*>& lists) { return merge(lists, 0, lists.size() - 1); } };
|
注意39行计算中位数的方法,通常的方法是 (l + r) / 2
,这种方法在l和r过大的时候可能会发生溢出的问题,因此更好的方法是 l + ((r - l) >> 1)
,需要特别注意的是注意后面的括号不要省略,因为加法的优先级更高。
时间复杂度
递推方程:$W(k) = W(k/2) + k/2 \times 2n, 其中n为每个链表的平均长度.$
解这个递推方程可以得到$W(k) = O(knlogk).$
另一种思路是将k个链表的头结点通过一个小顶堆来维护,每次取出最小的结点,然后将该结点下一个结点重新插入小顶堆中。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { auto cmp = [](const ListNode *lhs, const ListNode *rhs) { return lhs->val > rhs->val; }; priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq(cmp); ListNode dummy, *tail = &dummy; for (auto list : lists) if (list) pq.push(list); while (!pq.empty()) { tail->next = pq.top(); pq.pop(); tail = tail->next; if (tail->next) pq.push(tail->next); } return dummy.next; } };
|
时间复杂度
小顶堆中插入和删除操作的时间复杂度都是$O(logk)$,所有结点都需要进队一次,出队一次,因此算法的时间复杂度是$O(knlogk)$,和分治算法的时间复杂度一样。