1. 问题背景与核心思路
合并K个升序链表是算法面试中的经典问题,它考察了我们对数据结构的选择和操作能力。想象一下,你手头有多个已经按从小到大排好序的链表(比如多个班级的成绩单),现在需要把它们合并成一个大的有序链表。最直观的做法可能是两两合并,但这样效率不高。更聪明的做法是使用优先队列(堆)来帮助我们高效地完成这个任务。
优先队列就像一个智能的排队系统,它总是能让我们快速拿到当前最小的元素。在这个问题中,我们先把每个链表的第一个元素放入优先队列,然后每次取出最小的元素连接到结果链表,再把这个元素所在链表的下一个节点放入队列。这样就能保证我们总是处理当前最小的元素,最终得到一个完整的有序链表。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据结构与算法选择解析
2.1 为什么选择优先队列?
优先队列(通常用堆实现)能在O(1)时间获取最小/最大元素,插入和删除操作是O(log n)时间复杂度。对于合并K个链表的问题,我们需要频繁地获取当前最小的元素,这正是优先队列的强项。
对比其他方法:
- 暴力法:把所有节点值存入数组然后排序,时间复杂度O(N log N),空间复杂度O(N)
- 两两合并:时间复杂度O(KN),当K很大时效率低
- 优先队列法:时间复杂度O(N log K),空间复杂度O(K)
2.2 自定义比较函数的实现
在C++中,我们需要为优先队列定义一个比较函数,因为默认的优先队列是最大堆,而我们需要最小堆。代码中的cmp结构体重载了()运算符:
cpp复制struct cmp {
bool operator()(const ListNode *a, const ListNode *b) {
return a->val > b->val; // 实现最小堆
}
};
这里的小技巧是:虽然看起来是a>b,但实际上这样定义会让优先队列把较小的值放在顶部。这是因为C++的优先队列默认是最大堆,通过反转比较逻辑来实现最小堆。
3. 代码实现详解
3.1 初始化阶段
cpp复制priority_queue<ListNode*, vector<ListNode*>, cmp> pq;
for(auto node : lists) {
if(node) {
pq.
