LC23. 合并K个升序链表

题目

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
提示:

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

示例:

输入: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

思路

链表题目多涉及到节点调换、排序,有时需要考虑到头节点的转换问题。
因此做链表相关题目时进行建立带有表头的链表

一行一行合并

最直白的方法:
首先建立一个链表,依次合并lists中第1个链表、第二个链表……
但在做题时不敢使用,因为怕超时(计算错了时间复杂度)。

在此重新计算一下时间复杂度:

    k == lists.length
    0 <= k <= 10^4
    0 <= lists[i].length <= 500

假设数组中每一个链表都按照最大长度k=104来计算。
首先建立一个空链表,然后与第一个链表合并,合并后链表长度为n
然后与第二个链表合并,合并后链表长度变为2n
第i次合并后,链表长度变为in
则第i次合并的时间复杂度应为O((i-1)n)
总共需要合并n次,则总的时间复杂度为0+n+2n+……+kn=k*(0+nk)/2=k2n/2
去掉系数后,时间复杂度为O(k2n),这么计算的话时间是超时的。
但是此时应注意条件中:

    lists[i].length 的总和不超过 10^4

也就是说k*n最大为104,那么k2n就为108,不会出现超时情况。

分治

一行一行合并时间复杂度依然很大,而且每个时间段只合并两个链表太浪费时间了,我们需要提高一下时间利用率。这时可以采用分治思想
在这里插入图片描述通过递归可以很容易的完成上述功能:
通过递归调用将列表中链表分为两个两个一组,合并后回溯,得到大的链表,然后再与其他链表合并后的大链表进行合并,最终得到结果。
注意终止条件!

优先队列

优先队列和上述方法不同,该方法并不是进行合并。而是将所有链表放入一个优先队列(小顶堆)中依次弹出。

  • 首先建立一空链表ans
  • 建立一个结构体,用于构建优先队列。结构体中应包含指向当前链表节点的节点数值和节点指针
  • 将所有链表第一个节点入堆
  • 弹出最小值,将其节点加入ans中。再将弹出的链表节点的下一节点入堆
  • 直到堆中没有元素

这种方法实质是根据链表的第一个节点数值进行排序,再依次加入链表中。

代码

一行一行合并
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* ans = nullptr;
        int len = lists.size();
        for(int i=0;i<len;i++){
            ans = mergetwo(ans, lists[i]);
        }
        return ans;
    }
    ListNode* mergetwo(ListNode* h1, ListNode* h2){
        if(!h1||!h2) return h1? h1:h2;
        ListNode *ans = new ListNode(), *a = ans;
        while(h1&&h2){
            if(h1->val<=h2->val){
                a->next = h1;
                h1 = h1->next;
            }
            else{
                a->next = h2;
                h2 = h2->next;
            }
            a = a->next;
        }
        a->next = h1? h1:h2;
        return ans->next;
    }
};
分治
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return merge(lists, 0, lists.size()-1);
    }
    ListNode* merge(vector<ListNode*> lists, int left, int right){
        if(left>right) return nullptr;
        if(left==right) return lists[left];
        if(left==right-1) return mergetwo(lists[left], lists[right]);
        int mid = left +(right-left)/2;
        return mergetwo(merge(lists, left, mid), merge(lists, mid+1, right));
    }
    ListNode* mergetwo(ListNode* h1, ListNode* h2){
        if(!h1||!h2) return h1? h1:h2;
        ListNode ans, *a = &ans;
        while(h1&&h2){
            if(h1->val<h2->val){
                a->next = h1;
                h1 = h1->next;
            }
            else{
                a->next = h2;
                h2 = h2->next;
            }
            a = a->next;
        }
        a->next = h1? h1:h2;
        return ans.next;
    }
};
优先队列
class Solution {
public:
    struct story{
        int val;
        ListNode* list;
        bool operator < (const story &compare) const{
            return val > compare.val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size();
        priority_queue<story> q;
        ListNode ans, *a = &ans;
        for(auto ls:lists){
            if(ls)
                q.push({ls->val, ls});
        }
        while(!q.empty()){
            story t = q.top();
            q.pop();
            a->next = t.list;
            a = a->next;
            if(t.list->next!=nullptr)
                q.push({t.list->next->val, t.list->next});
        }
        a->next=nullptr;
        return ans.next;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现合并K个升序链表,我们可以考虑使用分治法,将K个链表划分为两个子问题,分别合并这两个子问题,然后不断递归下去。 具体实现过程如下: 1. 将K个链表按照长度平均划分为两个子问题,每个子问题递归调用合并函数,直到只剩下一个链表或两个链表。 2. 合并两个链表的过程可以使用归并排序中的合并函数,将两个链表合并为一个升序链表。 3. 将合并后的链表返回,然后递归回去继续合并两个子问题的结果。 Java代码实现如下: ``` public ListNode mergeKLists(ListNode[] lists) { if (lists == null || lists.length == 0) { return null; } return mergeKLists(lists, 0, lists.length - 1); } private ListNode mergeKLists(ListNode[] lists, int left, int right) { if (left == right) { return lists[left]; } int mid = (left + right) / 2; ListNode l1 = mergeKLists(lists, left, mid); ListNode l2 = mergeKLists(lists, mid + 1, right); return mergeTwoLists(l1, l2); } private ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null) { return l2; } if (l2 == null) { return l1; } if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l1, l2.next); return l2; } } ``` 其中,`mergeKLists` 函数是递归调用的入口函数,它接收一个 `ListNode` 数组作为参数,表示要合并的K个链表。在函数中,我们首先判断链表数组是否为空或长度为0,如果是,则返回 `null`。否则,我们调用 `mergeKLists` 函数,将链表数组划分为两个子问题,然后递归调用 `mergeKLists` 函数,继续划分子问题,直到只剩下一个链表或两个链表。 在 `mergeKLists` 函数中,我们使用归并排序的思想,将两个链表合并为一个升序链表。具体实现是在 `mergeTwoLists` 函数中,它接收两个链表作为参数,递归调用自身,将两个链表合并为一个升序链表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值