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 <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

思路一: 

先解决合并两个有序链表

假设链表 a 和 b 的长度都是 n,如何在 O(n) 的时间代价以及 O(1) 的空间代价完成合并?为了达到空间代价是 O(1),我们的宗旨是原地调整链表元素的 next 指针完成合并。

首先我们需要一个变量 head 来保存合并之后链表的头部,你可以把 head 设置为一个虚拟的头(也就是 head 的 val 属性不保存任何值),这是为了方便代码的书写,在整个链表合并完之后,返回它的下一位置即可。
我们需要一个指针 tail 来记录下一个插入位置的前一个位置,以及两个指针 aPtr 和 bPtr 来记录 a 和 b 未合并部分的第一位。注意这里的描述,tail 不是下一个插入的位置,aPtr 和 bPtr 所指向的元素处于待合并的状态,也就是说它们还没有合并入最终的链表。 当然也可以给他们赋予其他的定义,但是定义不同实现就会不同。


当 aPtr 和 bPtr 都不为空的时候,取 val 属性较小的合并;如果 aPtr 为空,则把整个 bPtr 以及后面的元素全部合并;bPtr 为空时同理。


在合并的时候,应该先调整 tail 的 next 属性,再后移 tail 和 *Ptr(aPtr 或者 bPtr)。那么这里 tail 和 *Ptr 是否存在先后顺序呢?它们谁先动谁后动都是一样的,不会改变任何元素的 next 指针。

本题可以用一个变量result来维护以及合并的链表,第 i 次循环把第 i 个链表和 result 合并,答案保存到 result 中。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* a,ListNode* b)
    {
        if((!a)||(!b))
        {
            return a ? a:b;
        }
        ListNode* dummyhead = new ListNode(0);
        ListNode* cur = dummyhead;
        ListNode* aptr = a;
        ListNode* bptr = b;
        while(aptr&&bptr)
        {
            if(aptr->val<bptr->val)
            {
                cur->next=aptr;
                aptr=aptr->next;
            }
            else
            {
                cur->next=bptr;
                bptr=bptr->next;
            }
            cur=cur->next;
        }
        cur->next=(aptr ? aptr:bptr);
        return dummyhead->next;
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* result = nullptr;
        for(int i=0;i<lists.size();i++)
        {
            result=mergeTwoLists(result,lists[i]);
        }
        return result;
    }
};

思路二:使用优先队列合并

我们需要维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

class Solution {
public:
    struct Status
    {
        int val;
        ListNode *ptr;
        bool operator < (const Status &rhs) const {
            return val > rhs.val;
        }
    };

    priority_queue<Status> q;

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        for(auto node:lists)
        {
            if(node)
            {
                q.push({node->val,node});
            }
        }
        ListNode head;
        ListNode* tail=&head;
        while(!q.empty())
        {
            auto f=q.top();
            q.pop();
            tail->next=f.ptr;
            tail=tail->next;
            if(f.ptr->next)
            {
                q.push({f.ptr->next->val,f.ptr->next});
            }
        }
        return head.next;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值