合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1->1->2->3->4->4->5->6
思路
在解决「合并K个排序链表」这个问题之前,我们先来看一个更简单的问题:如何合并两个有序链表?假设链表 和 的长度都是 ,如何在 的时间代价以及 的空间代价完成合并? 这个问题在面试中常常出现,为了达到空间代价是 ,我们的宗旨是「原地调整链表元素的 next
指针完成合并」。以下是合并的步骤和注意事项,对这个问题比较熟悉的读者可以跳过这一部分。此部分建议结合代码阅读。
head
来保存合并之后链表的头部,你可以把 head
设置为一个虚拟的头(也就是 head
的 val
属性不保存任何值),这是为了方便代码的书写,在整个链表合并完之后,返回它的下一位置即可。tail
来记录下一个插入位置的前一个位置,以及两个指针 aPtr
和 bPtr
来记录 和 未合并部分的第一位。注意这里的描述,tail
不是下一个插入的位置,aPtr
和 bPtr
所指向的元素处于「待合并」的状态,也就是说它们还没有合并入最终的链表。 当然你也可以给他们赋予其他的定义,但是定义不同实现就会不同。aPtr
和 bPtr
都不为空的时候,取 val
熟悉较小的合并;如果 aPtr
为空,则把整个 bPtr
以及后面的元素全部合并;bPtr
为空时同理。tail
的 next
属性,再后移 tail
和 *Ptr
(aPtr
或者 bPtr
)。那么这里 tail
和 *Ptr
是否存在先后顺序呢?它们谁先动谁后动都是一样的,不会改变任何元素的 next
指针。代码
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
复杂度
思路
我们可以想到一种最朴素的方法:用一个变量 ans
来维护以及合并的链表,第 次循环把第 个链表和 ans
合并,答案保存到 ans
中。
代码
class Solution {
public:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
<span class="hljs-function">ListNode* <span class="hljs-title">mergeKLists</span><span class="hljs-params">(<span class="hljs-built_in">vector</span><ListNode*>& lists)</span> </span>{
ListNode *ans = <span class="hljs-literal">nullptr</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i < lists.size(); ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
<span class="hljs-keyword">return</span> ans;
}
};
复杂度
ans
的长度为 ;第二次合并后,ans
的长度为 ,第 次合并后,ans
的长度为 。第 次合并的时间代价是 ,那么总的时间代价为 ,故渐进时间复杂度为 。思路
考虑优化方法一,用分治的方法进行合并。
代码
class Solution {
public:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
<span class="hljs-function">ListNode* <span class="hljs-title">merge</span><span class="hljs-params">(<span class="hljs-built_in">vector</span> <ListNode*> &lists, <span class="hljs-keyword">int</span> l, <span class="hljs-keyword">int</span> r)</span> </span>{
<span class="hljs-keyword">if</span> (l == r) <span class="hljs-keyword">return</span> lists[l];
<span class="hljs-keyword">if</span> (l > r) <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
<span class="hljs-keyword">int</span> mid = (l + r) >> <span class="hljs-number">1</span>;
<span class="hljs-keyword">return</span> mergeTwoLists(merge(lists, l, mid), merge(lists, mid + <span class="hljs-number">1</span>, r));
}
<span class="hljs-function">ListNode* <span class="hljs-title">mergeKLists</span><span class="hljs-params">(<span class="hljs-built_in">vector</span><ListNode*>& lists)</span> </span>{
<span class="hljs-keyword">return</span> merge(lists, <span class="hljs-number">0</span>, lists.size() - <span class="hljs-number">1</span>);
}
};
复杂度
思路
这个方法和前两种方法的思路有所不同,我们需要维护当前每个链表没有被合并的元素的最前面一个, 个链表就最多有 个满足这样条件的元素,每次在这些元素里面选取 val
属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。
代码
class Solution {
public:
struct Status {
int val;
ListNode *ptr;
bool operator < (const Status &rhs) const {
return val > rhs.val;
}
};
priority_queue <Status> q;
<span class="hljs-function">ListNode* <span class="hljs-title">mergeKLists</span><span class="hljs-params">(<span class="hljs-built_in">vector</span><ListNode*>& lists)</span> </span>{
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> node: lists) {
<span class="hljs-keyword">if</span> (node) q.push({node->val, node});
}
ListNode head, *tail = &head;
<span class="hljs-keyword">while</span> (!q.empty()) {
<span class="hljs-keyword">auto</span> f = q.top(); q.pop();
tail->next = f.ptr;
tail = tail->next;
<span class="hljs-keyword">if</span> (f.ptr->next) q.push({f.ptr->next->val, f.ptr->next});
}
<span class="hljs-keyword">return</span> head.next;
}
};
复杂度
原文:https://www.cnblogs.com/leetcodetijie/p/13091769.html