代码实现
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode dummy{};
ListNode* cur = &dummy;
while (list1 != nullptr && list2 != nullptr) {
if (list1->val < list2->val) {
cur->next = list1;
list1 = list1->next;
} else {
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
cur->next = (list1 != nullptr) ? list1 : list2;
return dummy.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists, int i, int j) {
int m = j - i;
if (m == 0) return nullptr;
if (m == 1) return lists[i];
ListNode* left = mergeKLists(lists, i, i + m / 2);
ListNode* right = mergeKLists(lists, i + m / 2, j);
return mergeTwoLists(left, right);
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
return mergeKLists(lists, 0, lists.size());
}
};
执行流程
示例输入
lists = [[1,4,5],[1,3,4],[2,6]]
rust复制编辑链表 1: 1 -> 4 -> 5
链表 2: 1 -> 3 -> 4
链表 3: 2 -> 6
合并过程
- 使用递归将 K 个链表两两合并。
mergeTwoLists
函数将两个排序链表合并成一个有序链表。mergeKLists
递归地将链表分成两半,合并每一对链表,直到最终得到一个合并后的排序链表。
最终输出
1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6
关键思路
- 递归分治法
- 将问题分解成更小的子问题。首先将 K 个链表分成两部分,递归地将每部分合并,最后合并结果。
- 每次合并两个链表,直到最后合并成一个完整的链表。
- 合并两个链表的技巧
- 使用虚拟头节点(
dummy
),可以避免处理头节点为空的情况,并简化链表操作。 - 使用指针
cur
追踪当前合并链表的位置。
- 递归合并 K 个链表
- 通过递归将链表数组
lists
分成两部分,分别合并,然后再合并两部分的结果。
时间 & 空间复杂度
操作 | 时间复杂度 | 空间复杂度 |
---|
合并两个链表 | O(n) | O(1) |
递归合并 K 个链表 | O(K×n) | O(logK) |
- 时间复杂度: 每次合并两个链表的时间复杂度是 O(n),其中 n是链表的长度。合并 K 个链表的时间复杂度是 O(K×n),其中 K是链表的数量。
- 空间复杂度: 递归调用栈的空间复杂度是 O(logK),因为我们每次递归将问题分为两半。
基础语法解析
1. ListNode dummy{}
- 虚拟头节点:使用一个虚拟头节点简化链表操作,避免了空链表情况下的特殊处理。
ListNode dummy{};
2. cur->next = list1;
- 合并操作:将当前较小的节点连接到新链表上,并移动对应的指针。
cur->next = list1;
3. return mergeTwoLists(left, right);
- 递归合并两部分:每次递归将链表分成两部分,分别合并,然后合并结果。
return mergeTwoLists(left, right);
4. cur = cur->next;
- 移动当前指针:在合并过程中,每次将较小的节点接入新链表时,需要移动当前指针。
cur = cur->next;
优化 & 变种
优化 1:使用优先队列(堆)来优化合并
- 使用最小堆(优先队列)可以在每次合并时保持当前最小元素,从而减少不必要的比较。通过优先队列可以实现更高效的合并。
#include <queue>
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<int, vector<int>, greater<int>> pq;
for (auto& list : lists) {
while (list != nullptr) {
pq.push(list->val);
list = list->next;
}
}
ListNode dummy;
ListNode* cur = &dummy;
while (!pq.empty()) {
cur->next = new ListNode(pq.top());
cur = cur->next;
pq.pop();
}
return dummy.next;
}
变种 1:使用迭代法合并 K 个链表
- 除了递归,还可以使用迭代法来合并 K 个链表,方法是依次将链表两两合并,直到所有链表合并为一个。
总结
C++ 语法 / 结构 | 作用 |
---|
ListNode dummy{} | 虚拟头节点,简化链表操作 |
cur->next = list1; | 合并两个链表的元素 |
return mergeTwoLists(left, right); | 递归合并左右两部分 |
时间复杂度: O(K \times n) | 合并 K 个链表的时间复杂度 |
空间复杂度: O(\log K) | 递归调用栈的空间复杂度 |