0
点赞
收藏
分享

微信扫一扫

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

芒果六斤半 2022-01-28 阅读 72

题目要求

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

题目要求如下:

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

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

示例如下:

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数组总链表数,因为递归调用会使用到栈空间。

举报

相关推荐

0 条评论