cpp
class Solution {
public:
// 主函数:负责启动分治过程
ListNode* mergeKLists(vector<ListNode*>& lists) {
// 1. 如果列表为空,直接返回空(防止空数组报错)
if (lists.empty()) return nullptr;
// 2. 调用分治函数,范围从 0 到最后一个索引
return solve(lists, 0, lists.size() - 1);
}
private:
// 分治函数:负责把 K 个链表不断二分
ListNode* solve(vector<ListNode*>& lists, int l, int r) {
// 3. 递归终止:如果区间里只有一个链表,直接返回它
if (l == r) return lists[l];
// 4. 计算中点,>> 1 相当于除以 2(位运算更快)
int mid = (l + r) >> 1;
// 5. 递归处理左边,递归处理右边,并把两边的结果通过 merge 函数合并
return merge(solve(lists, l, mid), solve(lists, mid + 1, r));
}
// 核心合并函数:合并两个有序链表(递归版)
ListNode* merge(ListNode* a, ListNode* b) {
// 6. 如果 a 空了,返回 b;如果 b 空了,返回 a(谁不空回谁)
if (!a || !b) return a ? a : b;
// 7. 如果 a 节点的值比较小
if (a->val < b->val) {
// 8. 让 a 的下一个位置去接"剩下的节点合并后的结果"
a->next = merge(a->next, b);
// 9. 选了 a 作为当前最小,返回 a
return a;
} else {
// 10. 如果 b 的值较小,同理让 b 的下一个位置去接合并结果
b->next = merge(a, b->next);
// 11. 选了 b 作为当前最小,返回 b
return b;
}
}
};
假设我们的 lists 中有 4个小链表:
-
L0 :
[1, 4] -
L1 :
[2, 6] -
L2 :
[0, 8] -
L3 :
[3, 7]
第一阶段:分治拆解 (solve 函数)
分治像一棵倒置的树,先把问题不断对半劈开。
-
第一层 :
solve(0, 3)-
计算
mid = 1。 -
拆分为左边
solve(0, 1)和右边solve(2, 3)。
-
-
第二层(左) :
solve(0, 1)-
计算
mid = 0。 -
拆分为
solve(0, 0)(直接返回 L0 )和solve(1, 1)(直接返回 L1)。
-
-
第二层(右) :
solve(2, 3)-
计算
mid = 2。 -
拆分为
solve(2, 2)(直接返回 L2 )和solve(3, 3)(直接返回 L3)。
-
第二阶段:递归合并 (merge 函数)
现在开始从底层向上"两两决斗"合并。
1. 合并 L0 [1, 4] 和 L1 [2, 6]
-
比较 1 和 2 :1 小,选 1。1 的后面去接
merge(L0的4, L1的2)。 -
比较 4 和 2 :2 小,选 2。2 的后面去接
merge(L0的4, L1的6)。 -
比较 4 和 6 :4 小,选 4。4 的后面去接
merge(null, L1的6)。 -
收尾:遇到 null,直接接上 6。
-
结果 (La) :
[1, 2, 4, 6]。
2. 合并 L2 [0, 8] 和 L3 [3, 7]
-
同理,经过比较:0 < 3,3 < 7,7 < 8。
-
结果 (Lb) :
[0, 3, 7, 8]。
第三阶段:终极对决 (La 和 Lb 合并)
这是最后一步:merge(La, Lb),即合并 [1, 2, 4, 6] 和 [0, 3, 7, 8]。
-
比较 1 和 0 :0 胜出。结果链表:
0 -> ... -
比较 1 和 3 :1 胜出。结果链表:
0 -> 1 -> ... -
比较 2 和 3 :2 胜出。结果链表:
0 -> 1 -> 2 -> ... -
比较 4 和 3 :3 胜出。结果链表:
0 -> 1 -> 2 -> 3 -> ... -
比较 4 和 7 :4 胜出。结果链表:
0 -> 1 -> 2 -> 3 -> 4 -> ... -
比较 6 和 7 :6 胜出。结果链表:
0 -> 1 -> 2 -> 3 -> 4 -> 6 -> ... -
收尾 :
La已经空了,把Lb剩下的[7, 8]直接贴在后面。
最终冠军 :[0, 1, 2, 3, 4, 6, 7, 8]
总结这个过程的记忆点
-
Solve 是"向下扎根":直到看到单个链表。
-
Merge 是"向上开花":把拿到的两个链表像拉拉链一样合起来。
-
递归的魅力:你不需要管中间有多少层,你只需要写好"两个怎么合",递归会自动帮你处理剩下的 K-2 个。