23. 合并 K 个升序链表

 


题目

题目链接:https://leetcode.cn/problems/merge-k-sorted-lists/

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

算法 = k 个指针分别指向 k 条链表 & 题目特征 = 合并俩个链表的升级版

合并 k 个有序链表的逻辑类似合并两个有序链表。

难点在于,如何快速得到 k 个节点中的最小节点,接到结果链表上?

维护一个最小节点的指针和对应的链表索引,依次选择最小的节点进行合并。

通过不断更新最小节点和链表指针,最终完成K个有序链表的合并。

假设有三个有序链表:

List 1: 1 -> 4 -> 5
List 2: 1 -> 3 -> 4
List 3: 2 -> 6

初始状态下,每个链表的指针如下:

List 1: 1 -> 4 -> 5
         ^
List 2: 1 -> 3 -> 4
         ^
List 3: 2 -> 6
         ^

首先,minNode和minPointer初始化为null和-1。

在第一次循环中,遍历三个链表,比较当前节点的值。minNode为null,所以将List 1的第一个节点1赋值给minNode,并将minPointer设为0(表示最小节点在List 1中)。

接下来,将minNode(即1)添加到结果链表中,然后更新List 1的指针,使其指向下一个节点。

结果链表:1
List 1: 4 -> 5
         ^
List 2: 1 -> 3 -> 4
         ^
List 3: 2 -> 6
         ^

在第二次循环中,再次遍历三个链表。minNode为1,minPointer为0(表示最小节点在List 1中)。

将minNode(即1)添加到结果链表中,然后更新List 2的指针,使其指向下一个节点。

结果链表:1 -> 1
List 1: 4 -> 5
              ^
List 2: 3 -> 4
         ^
List 3: 2 -> 6
         ^

在第三次循环中,再次遍历三个链表。minNode为1,minPointer为1(表示最小节点在List 2中)。

将minNode(即1)添加到结果链表中,然后更新List 1的指针,使其指向下一个节点。

结果链表:1 -> 1 -> 2
List 1: 4 -> 5
                   ^
List 2: 3 -> 4
              ^
List 3: 2 -> 6
         ^

继续循环直到所有链表遍历完毕。最终得到的合并后的有序链表为:1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6

完整代码:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int k = lists.size();                          // 通过传入的 vector lists,获取链表的数量k
        ListNode* dummyHead = new ListNode(0);         // 创建一个虚拟头结点dummyHead
        ListNode* tail = dummyHead;                    // 将尾指针tail指向dummyHead
        while (true) {                                 // 进入一个无限循环,直到跳出循环为止
            ListNode* minNode = nullptr;               // 定义minNode指针,用于记录当前最小节点
            int minPointer = -1;                       // 定义minPointer变量,用于记录对应的链表索引
            for (int i = 0; i < k; i++) {              // 遍历k个链表,判断当前链表指针lists[i]是否为空
                if (lists[i] == nullptr)               // 如果为空
                    continue;                          // 则继续遍历下一个链表
                if (minNode == nullptr || lists[i]->val < minNode->val) {  // 如果minNode为空 或者 lists[i]的值小于minNode的值
                    minNode = lists[i];                // 则更新minNode为lists[i]
                    minPointer = i;                    // minPointer为i
                }
            }
            if (minPointer == -1)                        // 如果minPointer仍然为-1,说明所有链表已经遍历完毕
                break;                                   // 跳出循环
            tail->next = minNode;                        // 将minNode连接到结果链表的尾部,即tail->next = minNode
            tail = tail->next;                           // 并更新tail指针为minNode
            lists[minPointer] = lists[minPointer]->next; // 更新lists[minPointer]为下一个节点
        }
        return dummyHead->next;                          // 返回合并后的链表的头节点
    }
};

算法 = 最小堆 & 题目特征 = 快速获取 k 个节点中的最小节点

合并 k 个有序链表的逻辑类似合并两个有序链表。

难点在于,如何快速得到 k 个节点中的最小节点,接到结果链表上?

把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点。

对比前一种方法。

  • 减少比较次数:在每次循环中,只需从优先级队列中取出最小节点,而不是遍历K个链表进行比较。

  • 无需每次更新最小节点的指针:在之前的方法中,通过遍历K个链表并比较节点值,确定最小节点后需要更新该链表的指针。而在最小堆的方法中,只需将最小节点的下一个节点加入优先级队列,无需显式更新指针。

这样可以减少比较的次数,提高效率。

class Solution {
public:
    struct compare {
    bool operator()(const ListNode* a, const ListNode* b) {
        return a->val > b->val;
    }
};

ListNode* mergeKLists(vector<ListNode*>& lists) {
    if (lists.size() == 0) return nullptr;
    // 虚拟头结点
    ListNode* dummy = new ListNode(-1);
    ListNode* p = dummy;
    // 优先级队列,最小堆
    priority_queue<ListNode*, vector<ListNode*>, compare> pq;
    // 将 k 个链表的头结点加入最小堆
    for (ListNode* head : lists) {
        if (head != nullptr)
            pq.push(head);
    }

    while (!pq.empty()) {
        // 获取最小节点,接到结果链表中
        ListNode* node = pq.top();
        pq.pop();
        p->next = node;
        if (node->next != nullptr) 
            pq.push(node->next);
        // p 指针不断前进
        p = p->next;
    }
    return dummy->next;
	}
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值