leetcode23:合并K个升序链表

题目链接

leetcode

题目描述

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

C++ 代码

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 链表结点的定义
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) {}
};

// 辅助函数:创建链表
ListNode* createList(const vector<int>& nums) {
ListNode* dummy = new ListNode(-1);
ListNode* cur = dummy;
for (int num : nums) {
cur->next = new ListNode(num);
cur = cur->next;
}
return dummy->next;
}

// 辅助函数:创建链表数组
vector<ListNode*> createList(const vector<vector<int>>& nums) {
vector<ListNode*> lists;
for (const auto& num : nums) {
lists.push_back(createList(num));
}
return lists;
}

// 辅助函数:打印链表
void printList(ListNode* head) {
while (head != nullptr) {
cout << head->val;
if (head->next != nullptr) cout << " -> ";
head = head->next;
}
cout << endl;
}

// 辅助函数:释放链表内存
void deleteList(ListNode* head) {
while (head != nullptr) {
ListNode* temp = head;
head = head->next;
delete temp;
}
}

/*
K 个升序链表中,每个链表的头结点的地址放入一个小根堆。
当小根堆不为空时,取出小根堆的第一个结点,
若该结点的下一个结点不为空,则放入堆;
否则,不放入堆。

时间复杂度:O(n*k*logk)
n 表示每个升序链表的长度,k 表示升序链表的个数。
优先队列中的元素不超过 k 个,插入和删除的代价为 O(logk),
这里最多有 n*k 个点,每个结点都被插入删除各一次。
空间复杂度:O(k)
优先队列中的元素不超过 k 个
*/
class Solution_0 {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
// 自定义比较函数
struct Compare {
bool operator()(ListNode* l, ListNode* r) {
return l->val > r->val; // 小根堆,返回 >
}
};
priority_queue<ListNode*, vector<ListNode*>, Compare> pq;

// 将所有链表的头结点放入优先队列
for (ListNode* head : lists) {
if (head != nullptr) {
pq.push(head);
}
}

// 依次从优先队列中取出最小的结点,并更新链表
ListNode *dummy = new ListNode(), *tail = dummy;
while (!pq.empty()) {
ListNode* cur = pq.top(); pq.pop();
tail->next = cur;
tail = tail->next;
if (cur->next != nullptr) {
pq.push(cur->next);
}
}

return dummy->next;
}
};

/*
分治法合并,优化空间复杂度

将 k 个链表分成两半,分别合并,然后合并这两个结果。
重复这个过程,直到只剩下一个链表,即为最终结果。

时间复杂度:O(n*k*logk)
设链表数组中有 k 个链表,每个链表的平均长度为 n。
在最坏情况下,需要合并所有的链表,每层递归都会合并 k/2 个链表。
递归的层数是 k*logk,每层递归合并链表时的平均时间复杂度是 n,
所以总的时间复杂度是 O(n*k*logk)。
空间复杂度:O(logk)
每次递归会将问题规模减半,递归调用使用到栈空间是 O(logk)。
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size();
if (n == 0) {
return nullptr;
}
if (n == 1) {
return lists[0];
}

// 迭代实现
int interval = 1;
while (interval < n) {
for (int i = 0; i + interval < n; i += interval*2) {
lists[i] = mergeTwoLists(lists[i], lists[i+interval]);
}
interval *= 2;
}
return lists[0];
}

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *dummy = new ListNode(-1), *p = dummy;

while (l1 && l2) {
if (l1->val <= l2->val) {
p->next = l1;
l1 = l1->next;
} else {
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 != nullptr ? l1 : l2;

return dummy->next;
}
};

// 辅助函数:打印二维数组
void printArray(const vector<vector<int>>& nums) {
cout << "[";
for (size_t i = 0; i < nums.size(); i++) {
cout << "[";
for (size_t j = 0; j < nums[i].size(); j++) {
cout << nums[i][j];
if (j != nums[i].size() - 1) cout << ",";
}
cout << "]";
if (i != nums.size() - 1) cout << ",";
}
cout << "]";
}

int main() {
Solution solution;
vector<vector<vector<int>>> lists_cases = {
{{1,4,5}, {1,3,4}, {2,6}},
{},
{{}}
};

for (const auto& nums : lists_cases) {
vector<ListNode*> lists = createList(nums);
cout << "Input: ";
printArray(nums);
cout << endl;

ListNode* mergedList = solution.mergeKLists(lists);
cout << "Output: ";
printList(mergedList);
}

return 0;
}

leetcode23:合并K个升序链表
https://lcf163.github.io/2023/10/02/leetcode23:合并K个升序链表/
作者
乘风的小站
发布于
2023年10月2日
许可协议