这是我参与更文挑战的第 27 天,活动详情查看更文挑战
题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k
==lists.length
- 0 <=
k
<= - 0 <=
lists[i].length
<= 500 - <=
lists[i][j]
<= lists[i]
按 升序 排列lists[i].length
的总和不超过
解题思路
在解决「合并K个排序链表」这个问题之前,我们先来看一个更简单的问题:如何合并两个有序链表?假设链表 a
和 b
的长度都是 n
,如何在 的时间代价以及 的空间代价完成合并? 这个问题在面试中常常出现,为了达到空间代价是 ,我们的宗旨是「原地调整链表元素的 next
指针完成合并」。以下是合并的步骤和注意事项,对这个问题比较熟悉的读者可以跳过这一部分。此部分建议结合代码阅读。
首先我们需要一个变量 head
来保存合并之后链表的头部,你可以把 head
设置为一个虚拟的头(也就是 head
的 val
属性不保存任何值),这是为了方便代码的书写,在整个链表合并完之后,返回它的下一位置即可。
我们需要一个指针 tail
来记录下一个插入位置的前一个位置,以及两个指针 aPtr
和 bPtr
来记录 a
和 b
未合并部分的第一位。注意这里的描述,tail
不是下一个插入的位置,aPtr
和 bPtr
所指向的元素处于「待合并」的状态,也就是说它们还没有合并入最终的链表。 当然你也可以给他们赋予其他的定义,但是定义不同实现就会不同。
当 aPtr
和 bPtr
都不为空的时候,取 val
熟悉较小的合并;如果 aPtr
为空,则把整个 bPtr
以及后面的元素全部合并;bPtr
为空时同理。
在合并的时候,应该先调整 tail
的 next
属性,再后移 tail
和 *Ptr
(aPtr
或者 bPtr
)。那么这里 tail
和 *Ptr
是否存在先后顺序呢?它们谁先动谁后动都是一样的,不会改变任何元素的 next
指针
代码
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;
}
ListNode* merge(vector <ListNode*> &lists, int l, int r) {
if (l == r) return lists[l];
if (l > r) return nullptr;
int mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
return merge(lists, 0, lists.size() - 1);
}
};