【LeetCode】23.合并k个升序链表

题目链接

LeetCode 23.合并k个升序链表

题目描述

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

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

示例1

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例2

输入:lists = []
输出:[]

示例3

输入:lists = [[]]
输出:[]

提示

  • k == lists.length
  • 0 <= k <= 104
  • 0 <= lists[i].length <= 500
  • -104 <= lists[i][j] <= 104
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

算法

与合并两个有序序列的思路一致。

从以上思路出发,考虑合并两个有序链表时候的算法:每次找到两个链表中当前较小的一个,将其合并到结果中。则合并k个有序链表时候,每次从k个链表节点中找到值最小的一个,将其合并到答案中。

要维护多个元素的最小值,则可以自然考虑到堆。

C++中的堆是priority_queue,要使用小根堆需要在定义的时候指定排序,同时要优先队列维护链表节点,需要自己重新写排序方法。

在题解中使用了类中重载operator()的方法来自己手写排序方法。

实现中的细节

除了第一个节点,其他的节点应当是让当前节点的next指向下一个找到的节点。则需要对第一个节点特殊处理,如果使用虚拟头结点,则可以避免特殊处理头结点的情况。即new一个dummy节点作为虚拟头结点,使用一个指针指向虚拟头结点,则所有节点的操作都变成了找到值最小的节点,插入到当前节点的后面。

从上面的示例3可以看到,链表向量中有空链表,这种则不应当加入到堆中。

复杂度分析

时间复杂度

链表有 k k k个,则堆中的元素最大有 k k k个,对于堆的插入和删除操作,复杂度是 O ( l o g k ) O(log k) O(logk)
链表长度为 n n n,则所有链表的元素的个数总和最多有 k × n k \times n k×n个,对于每个点都需要插入和删除一次。
综上,时间复杂度为 O ( k × n × l o g k ) O(k \times n \times log k) O(k×n×logk)

空间复杂度

堆中的元素最多等于链表的个数 k k k,其他的变量都是常数个。
即空间复杂度为 O ( k ) O(k) O(k)

C++代码

class Solution {
public:
    class listGreater
    {
    public:
        bool operator()(const ListNode* p1, const ListNode* p2)
        {
            return p1->val > p2->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, listGreater> q;
        for(auto l: lists) 
        {
            if(l)
                q.push(l);
        }
        ListNode* dummy = new ListNode();
        ListNode* ptr = dummy;
        while(!q.empty())
        {
            ListNode* t = q.top();
            q.pop();
            ptr->next = t;
            ptr = ptr->next;
            if(t->next) q.push(t->next);
        }
        ptr = dummy->next;
        delete dummy;
        return ptr;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值