LeetCode 23.合并 K 个升序链表(分治递归)

LeetCode23|合并 K 个升序链表(分治递归)

一、题目大意

给一个链表数组lists,数组里面每一条链表都已经是升序。把全部链表合并成一条升序链表 ,返回合并后的链表。 输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:1→1→2→3→4→4→5→6 边界:lists = []lists=[[]],输出空链表。

二、核心思路(分治归并,和 148 思想一致)

  1. :对链表数组的下标区间对半拆分,递归处理左区间、右区间,直到区间只剩 1 条链表直接返回;区间无元素返回null
  2. :左右区间各自递归完成后,得到两条已经有序的链表,复用LeetCode21 合并两个有序链表,将两条链表合并。

时间复杂度:(O(knlog k)),k 链表条数,n 单链表平均节点数 空间复杂度:(O(log k)),递归栈开销

对比朴素循环两两合并:逐条往结果上合并,时间(O(k^2n)),大数据会超时,不推荐面试写。

三、完整 Java 代码

ini 复制代码
/**
 * Definition for singly‑linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0) {
            return null;
        }
        return merge(lists, 0 , lists.length - 1);
    }

    //递归:合并lists数组 [l, r]下标区间内所有链表
    private ListNode merge(ListNode[] lists, int l, int r) {
        if(l > r) {
            return null;
        }
        if(l == r) {
            return lists[l];
        }
        int mid = (l + r) / 2;
        ListNode leftPart = merge(lists, l, mid);
        ListNode rightPart = merge(lists, mid + 1, r);
        return mergeTwoLists(leftPart, rightPart);
    }

    //LeetCode21:合并两个升序链表
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode tail = dummy;
        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                tail.next = l1;
                l1 = l1.next;
            } else {
                tail.next = l2;
                l2 = l2.next;
            }
            tail = tail.next;
        }
        //拼接剩余部分
        if(l1 != null) {
            tail.next = l1;
        } else {
            tail.next = l2;
        }
        return dummy.next;
    }
}

四、实例演示(表格)

输入:lists = [1→4→5 ,1→3→4 ,2→6],下标 0、1、2,调用merge(lists,0,2)

递归调用 l r mid 操作说明 返回值
merge(0,2) 0 2 1 0,12,2 mergeTwoLists (左结果,右结果)
├─merge(0,1) 0 1 0 0,01,1 mergeTwoLists (左结果,右结果)
│ ├─merge(0,0) 0 0 - l==r直接返回 lists 0 1→4→5
│ └─merge(1,1) 1 1 - l==r直接返回 lists 1 1→3→4
│ 合并两条链表 - - - mergeTwoLists 1→1→3→4→4→5
└─merge(2,2) 2 2 - l==r直接返回 lists 2 2→6
顶层合并 - - - mergeTwoLists 1→1→2→3→4→4→5→6

递归树

scss 复制代码
                 merge(0‑2)
                ↙         ↘
        merge(0‑1)        merge(2‑2)
       ↙        ↘             ↓
merge(0‑0)    merge(1‑1)    2→6
    ↓             ↓
1→4→5         1→3→4
       ↘       ↙
  mergeTwoLists合并
       ↓
1→1→3→4→4→5
           ↘         ↙
           mergeTwoLists合并
                 ↓
        1→1→2→3→4→4→5→6

五、语法坑与边界条件

  1. 分清两个函数:

    • merge(lists,l,r):接收数组 + 下标,递归分治;
    • mergeTwoLists(l1,l2):只接收两个链表节点,做两两合并。不要传数组进 mergeTwoLists,会编译报错
  2. if(l > r) return null:拆分后区间不存在,没有链表,返回空;mergeTwoLists可以正常接收null

  3. if(l == r) return lists[l]:区间只剩 1 条链表,直接返回,不再拆分。

  4. mergeTwoLists 内部,tail = tail.next不能漏写。

  5. 边界处理:数组为空lists.length==0直接返回 null;数组里面存放空链表null也可以被 mergeTwoLists 正常处理。

六、个人感悟

  1. 本题是21 题的升级版,分治思想和 148 排序链表自顶向下归并一模一样,旧代码复用。
  2. 朴素循环两两合并写法简单,但是时间效率差,面试优先写分治版本。
  3. 递归终止条件两个:l>r(无元素)、l==r(单个元素),两个缺一不可。
  4. 还有小根堆(优先队列)解法,也是(O(knlog k)),适合了解思路。
相关推荐
Nil2084 小时前
leetcode 105从前序和中序遍历序列构造二叉树
算法·leetcode·职场和发展
Nil2085 小时前
leetcode 114二叉树展开为链表
leetcode·链表·深度优先
cz07105 小时前
hot100_搜索二维矩阵 II
算法·leetcode
圣保罗的大教堂7 小时前
leetcode 3718. 缺失的最小倍数 简单
leetcode
青 春 记 忆7 小时前
LeetCode 234. 回文链表|Python 解法详解
python·leetcode·链表
啊嘞嘞?9 小时前
力扣(LRU缓存)
算法·leetcode
啊嘞嘞?9 小时前
力扣(下一个排列)
算法·leetcode
Forever Nore10 小时前
LeetCode 16 最接近的三数之和 - 双指针逼近
算法·leetcode·职场和发展
smj2302_7968265210 小时前
解决leetcode第4033题有效K个不同元素子数组I
数据结构·python·算法·leetcode