当前位置: 首页 > news >正文

LeetCode 23:合并 K 个升序链表

LeetCode 23:合并 K 个升序链表

在这里插入图片描述

问题本质:多有序链表的归并

给定 K升序链表,需合并为一个全局升序链表,返回其头节点。核心是高效处理多链表的有序合并,避免暴力法的高复杂度。

核心思路:两种高效解法

1. 优先队列(最小堆)
  • 核心逻辑:维护当前所有链表头节点的最小值,每次取最小节点加入结果,再将该节点的下一个节点(若存在)加入堆,循环至所有节点处理完毕。
  • 优势:利用堆的排序特性,保证每次取最小值的时间为 O(logK),整体高效。
2. 分治合并
  • 核心逻辑:类似归并排序,将 K 个链表递归拆分为两两一组,合并后再递归合并结果,最终得到全局有序链表。
  • 优势:递归结构清晰,空间复杂度更优(递归栈深度为 O(logK))。

方法一:优先队列(最小堆)详解

步骤 1:处理边界情况
  • lists 为空或所有链表为空,直接返回 null
步骤 2:初始化优先队列
  • 使用 PriorityQueue 实现最小堆,比较器按节点值从小到大排序。
  • 遍历 lists,将非空链表的头节点加入堆(过滤空链表,避免空指针)。
步骤 3:构建结果链表
  • 创建虚拟头节点dummy),简化链表拼接(无需处理头节点为空的边界)。
  • 循环处理堆:
    1. 取出堆顶元素(当前最小节点),接入结果链表。
    2. 若该节点有下一个节点,将其加入堆(维持堆的动态更新)。
步骤 4:返回结果
  • 虚拟头节点的 next 即为合并后链表的头节点。

方法二:分治合并详解

步骤 1:递归终止条件
  • 若链表数组为空,返回 null
  • 若只剩一个链表,直接返回该链表(递归基例)。
步骤 2:递归拆分
  • 将链表数组分为左右两半,递归合并左右部分,得到两个局部合并后的链表 l1l2
步骤 3:合并两个有序链表
  • 实现辅助函数 mergeTwoLists,用双指针法合并两个有序链表:
    • 比较两链表当前节点值,将较小值接入结果链表,指针后移。
    • 处理剩余未遍历的节点(直接拼接)。
步骤 4:递归合并结果
  • 合并 l1l2,返回最终结果。

完整代码实现

方法一:优先队列(最小堆)
/*** 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;// 初始化最小堆,按节点值排序PriorityQueue<ListNode> minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);// 将非空链表的头节点加入堆for (ListNode head : lists) {if (head != null) {minHeap.offer(head);}}// 虚拟头节点,简化拼接ListNode dummy = new ListNode(-1);ListNode curr = dummy;// 处理堆中的节点while (!minHeap.isEmpty()) {ListNode minNode = minHeap.poll(); // 取出当前最小节点curr.next = minNode;              // 接入结果链表curr = curr.next;                 // 指针后移// 将下一个节点加入堆(若存在)if (minNode.next != null) {minHeap.offer(minNode.next);}}return dummy.next;}
}
方法二:分治合并
/*** 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);}// 递归合并区间 [left, right] 的链表private ListNode merge(ListNode[] lists, int left, int right) {if (left == right) {return lists[left]; // 单个链表,直接返回}int mid = left + (right - left) / 2; // 避免整数溢出ListNode l1 = merge(lists, left, mid);ListNode l2 = merge(lists, mid + 1, right);return mergeTwoLists(l1, l2);}// 合并两个有序链表(双指针法)private ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(-1);ListNode curr = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {curr.next = l1;l1 = l1.next;} else {curr.next = l2;l2 = l2.next;}curr = curr.next;}// 拼接剩余节点(其中一个链表已遍历完)curr.next = (l1 != null) ? l1 : l2;return dummy.next;}
}

复杂度分析

优先队列法
  • 时间复杂度O(N logK),其中 N 是所有链表的总节点数,K 是链表个数。每个节点入堆、出堆各一次,堆操作时间为 O(logK)
  • 空间复杂度O(K),堆中最多存储 K 个节点(初始头节点数)。
分治合并法
  • 时间复杂度O(N logK),每层合并的总节点数为 N,共 logK 层(递归深度)。
  • 空间复杂度O(logK),递归栈的深度为 logK(分治层数),额外空间为 O(1)(双指针合并的虚拟节点)。

示例验证

示例 1
输入:lists = [[1,4,5],[1,3,4],[2,6]]

  • 优先队列法:堆初始包含 112,每次取最小节点,依次拼接,最终得到 1→1→2→3→4→4→5→6
  • 分治合并法:递归拆分合并,最终结果一致。

总结

两种方法均能高效解决问题,核心是利用排序或分治降低复杂度

  • 优先队列:直观高效,适合处理动态更新的最小节点选取。
  • 分治合并:递归简洁,空间更优,体现“分而治之”的算法思想。

根据场景选择:若需快速实现,优先队列更直接;若追求空间优化,分治合并更优。两者时间复杂度一致,均为 O(N logK),是解决多有序链表合并的经典方案。

http://www.dtcms.com/a/296011.html

相关文章:

  • QT框架,关于WebEngine打包
  • DPU 是什么?
  • 达梦数据库报错“回滚记录版本太旧,无法获取用户记录”问题根源原理和解决方法
  • JVM原理及其机制(二)
  • Python图像数据处理
  • 浅谈程序运行之编译和链接 - 翻译环境和运行环境
  • go语言基础教程:1. Go 下载安装和设置
  • JVM Java虚拟机
  • JVM 垃圾收集器CMS和G1
  • 单片机的硬件结构
  • 人形机器人加快先进AI机器人开发
  • Spring Boot 全方位指南:从项目初始化到分层架构搭建
  • 设置后轻松将 iPhone 转移到 iPhone
  • 管网监测是指什么?管网监测的内容与应用场景
  • 小程序生命周期及页面操作执行过程详解
  • jupyter lab使用(linux环境)
  • 在 Windows 上安装设置 MongoDB及常见问题
  • MySQL--day13--视图存储过程与函数
  • Spring-狂神说
  • 2025年6月GESP(C++六级):最大因数
  • 传染病监测(七):为什么接触模式能颠覆防控效果预测?
  • 力扣刷题844——比较含退格的字符串
  • 【C语言进阶】柔性数组
  • 电商项目_秒杀_架构升级
  • 第十八天(Linux基本命令)
  • ollama无法拉取模型导致报错
  • Spring AI 系列之二十三 - AudioModels
  • 20250724-day21
  • 自己开发VUE之web网页打包技术毕业论文——仙盟创梦IDE
  • 通过为前端项目接入GeoGebra,初步研究AI时代数学教案的生成方案