LeetCode Hot100【链表-23. 合并 K 个升序链表】

题目:23. 合并 K 个升序链表

代码实现
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;  // 移动 list1 指针
            } else {
                cur->next = list2;  // 将较小的节点连接到当前节点
                list2 = list2->next;  // 移动 list2 指针
            }
            cur = cur->next;  // 移动当前指针
        }

        // 将剩余的部分连接到当前链表
        cur->next = (list1 != nullptr) ? list1 : list2;
        return dummy.next;  // 返回合并后的链表头
    }

    // 合并 K 个排序链表,递归方法
    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);
    }

    // 合并 K 个排序链表,调用递归函数
    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
合并过程
  1. 使用递归将 K 个链表两两合并。
  2. mergeTwoLists 函数将两个排序链表合并成一个有序链表。
  3. mergeKLists 递归地将链表分成两半,合并每一对链表,直到最终得到一个合并后的排序链表。
最终输出
1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6

关键思路

  1. 递归分治法
    • 将问题分解成更小的子问题。首先将 K 个链表分成两部分,递归地将每部分合并,最后合并结果。
    • 每次合并两个链表,直到最后合并成一个完整的链表。
  2. 合并两个链表的技巧
    • 使用虚拟头节点(dummy),可以避免处理头节点为空的情况,并简化链表操作。
    • 使用指针 cur 追踪当前合并链表的位置。
  3. 递归合并 K 个链表
    • 通过递归将链表数组 lists 分成两部分,分别合并,然后再合并两部分的结果。

时间 & 空间复杂度

操作时间复杂度空间复杂度
合并两个链表O(n)O(1)
递归合并 K 个链表O(K×n)O(log⁡K)
  • 时间复杂度: 每次合并两个链表的时间复杂度是 O(n),其中 n是链表的长度。合并 K 个链表的时间复杂度是 O(K×n),其中 K是链表的数量。
  • 空间复杂度: 递归调用栈的空间复杂度是 O(log⁡K),因为我们每次递归将问题分为两半。

基础语法解析

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)递归调用栈的空间复杂度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值