LeetCode 23. 合并K个升序链表 && 有序链表的合并

题目要求

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

题目要求如下:

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

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

示例如下:

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

ListNode节点结构如下:

  public class ListNode {
      int val;
      ListNode next;
      ListNode() {}
      ListNode(int val) { this.val = val; }
      ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  }

解法:归并

思路

首先观察问题描述,不难看出此问题的子问题就是合并两个有序链表,所以不妨先解决合并两个有序链表的问题,再来考虑怎么合并多个有序链表。

合并两个有序链表

合并两个有序链表需要一个额外的链表头head,因为链表有序,只需要从头遍历两个链表,并对两个链表的节点进行值比较,将val值较小的节点直接尾插到链表头head后,依此进行,直到某一链表遍历完毕,再将另一链表的剩余部分直接接到新链表尾部即可。

合并过程示意图如下:
在这里插入图片描述
合并两个链表的方法代码如下:

	public ListNode mergeList(ListNode list1, ListNode list2){
        ListNode head = new ListNode();
        ListNode last = head;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                last.next = list1;
                list1 = list1.next;
            }else{
                last.next = list2;
                list2 = list2.next;
            }
            last = last.next;
        }
        if(list1 != null) last.next = list1;
        if(list2 != null) last.next = list2;
        return head.next;
    }

直接合并多个有序链表

首先最容易想到且最为简单的思路就是,设定一个新的链表result,初始可以赋值为lists[0],之后遍历lists数组,将每一次遍历的链表lists[i]与result进行合并,遍历完成后result合并完成,直接返回result。

复杂度分析

时间复杂度:O(k²n)设链表数组内每个链表的最长长度为你n。第一次合并result链表长度为n,第二次合并时为2 × n,可以推知第k次合并时result的长度为k × n,且总共需要k次合并。故总时间代价为O(n × (1+k) / 2 × k)。
空间复杂度:O(1),直接合并方式的空间复杂度优秀,只需要常数级别的额外存储空间。

归并合并多个有序链表

直接合并在时间开销上的缺点显而易见,即一次循环只合并两个链表,如果每次循环合并多个链表,即归并合并,每次将相邻的链表两两合并,总共需要log k次循环就可以合并。

归并合并示意图如下:
在这里插入图片描述
图片源自LeetCode,跳转链接

完整AC代码

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        return merge(lists, 0, lists.length - 1);
    }
	// 分治
    public ListNode merge(ListNode[] lists, int l, int r){
        if(l == r) return lists[l];
        if(l > r) return null;
        int mid = (l + r) / 2;
        return mergeList(merge(lists, l, mid), merge(lists, mid + 1,r));
    }

	// 合并两个有序链表
    public ListNode mergeList(ListNode list1, ListNode list2){
        ListNode head = new ListNode();
        ListNode last = head;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                last.next = list1;
                list1 = list1.next;
            }else{
                last.next = list2;
                list2 = list2.next;
            }
            last = last.next;
        }
        if(list1 != null) last.next = list1;
        if(list2 != null) last.next = list2;
        return head.next;
    }
}

复杂度分析

时间复杂度:O(log k × kn)仍设每个链表最长长度为n,第一轮合并需要合并k/2组链表,则每一组合并的时间复杂度为O(2n),第二组要合并k/4组链表,同时每一组合并的时间复杂度为O(4n),因此平均每一轮合并的时间复杂度为O(kn),总共进行log k次合并,故总时间复杂度为O(log k × kn)。
空间复杂度:O(log k),其中k为lists数组总链表数,因为递归调用会使用到栈空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7rulyL1ar

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值