今日又是记录一道链表归并的题目------23. 合并 K 个升序链表 - 力扣(LeetCode)

首先可以想到的一种简单的思路,便是用一个哨兵位的头节点将数组的首位个链表连接寄来,然后依次遍历首链表之后每个链表,每次遍历将各个链表进行合并到哨兵位指向的链表:
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size();
if (n == 0)
return nullptr;
ListNode t(-1, lists[0]);
ListNode* phead = &t;
for (int i = 1; i < n; ++i)
{
ListNode* prev = phead, *l1 = prev->next, *l2 = lists[i];
while (l1 && l2)
{
if (l1->val <= l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
if (l1)
prev->next = l1;
if (l2)
prev->next = l2;
}
return phead->next;
}
};
但是显然该解法的时间复杂度是O(N * K) , 不够优雅。是否存在时间复杂度更低的解法。有的,此处可以使用归并排序的方法解决,此时会存在一个问题:此处数组存储的是一个个的链表,在归并的过程中,会将两个链表合并为一个,也就是说数组中会出现空缺的位置。所以就需要约定将合并完成的链表放到一个固定的位置,让下次归并时可以直接找到该链表,这里约定每次合并好之后将·链表放到左区间的首位置(也就是单趟归并的整个区间的首位)。
以下是代码实现:
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
void MergeSortLists(vector<ListNode*>& lists, int begin, int end)
{
if (begin >= end)
return;
int mid = begin + (end - begin) / 2;
int left1 = begin, left2 = mid + 1;
MergeSortLists(lists, left1, mid);
MergeSortLists(lists, left2, end);
ListNode t(-1, lists[begin]);
ListNode* phead = &t, *prev = phead;
ListNode *l1 = lists[left1], *l2 = lists[left2];
while (l1 && l2)
{
if (l1->val <= l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
if (l1)
prev->next = l1;
if (l2)
prev->next = l2;
//防止前一个链表为空的情况,直接将合并完成的链表放到数组中
lists[begin] = phead->next;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size();
if (n == 0)
return nullptr;
MergeSortLists(lists, 0, n - 1);
return lists[0];
}
};
虽然时间复杂度降到了O(NlogK), 但是上述解法同时存在O(logN) 的函数栈帧的开销,因此可以使用非递归版本:
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size();
if (n == 0)
return nullptr;
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin = i, end = min(i + 2 * gap, n) - 1;
int left1 = begin, left2 = i + gap;
if (left2 > end)
break;
ListNode t(-1, lists[begin]);
ListNode* phead = &t, *prev = phead;
ListNode *l1 = lists[left1], *l2 = lists[left2];
while (l1 && l2)
{
if (l1->val <= l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
if (l1)
prev->next = l1;
if (l2)
prev->next = l2;
//防止前一个链表为空的情况,直接将合并完成的链表放到数组中
lists[begin] = phead->next;
}
gap *= 2;
}
return lists[0];
}
};