(Java)leetcode-23 Merge k Sorted Lists(合并K个排序链表)

2023-11-12

题目描述

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

思路1:两两合并

参考21 - 合并两个有序链表

合并两个有序链表有两种方法:

  • (1)迭代
  public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
   	//判断链表是否为空
       if (l1 == null) {
           return l2;
       }
       if (l2 == null) {
           return l1;
       }
       ListNode head = new ListNode(0) ;
       ListNode p = head;//p始终指向新链表的末端

       while(l1 != null && l2 != null){
       	if(l1.val < l2.val){
       		p.next = l1;       		
       		l1 = l1.next;
       	}
       	else{
       		p.next = l2;       		
       		l2 = l2.next;
       	}
       	p = p.next;
       }
       //连接剩余部分
	p.next = l1 != null ? l1 : l2;
       return head.next;
   }

  • (2)递归
public 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;
		}
    }

逐一合并

那么此题的K个链表,可以对它们依次进行合并,一共需要进行K-1次合并,时间复杂度O(KN):

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode res = null;
        for (ListNode list: lists) {
            res = mergeTwoLists(res, list); //调用合并2链表的方法
        }
        return res;
    }
}

分治合并

对 逐一合并 进行优化,用分治的方法进行合并——

  • 将 k个链表配对并将同一对中的链表合并;
  • 第一轮后,k个链表被合并为k/2个,平均长度为2n/k,继续。。。
  • 重复这一过程,直到我们得到了最终的有序链表

时间复杂度分析:K条链表的总结点数是 N,平均每条链表有 N/K个节点,因此合并**两条链表**的时间复杂度是 O(N/K)。从 K条链表开始两两合并成 1 条链表,因此每条链表都会被合并 logK 次,因此 K 条链表会被合并 K * logK次,因此总共的时间复杂度是 K* logK *N/K 即 O(NlogK)。

  • 空间复杂度:递归会使用到 O(log k)空间代价的栈空间。

分治合并——迭代

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        // 需要合并的链表数量
        int k = lists.length;

        while (k > 1) {
        	// idx指向合并后的链表应该存放的位置
            int idx = 0;
            // 每一轮将k条链表合并为k/2条
            for (int i = 0; i < k; i += 2) {
            	// i指向最后一条链表
                if (i == k - 1) {
                    lists[idx++] = lists[i];
                } else {
                	// 合并两条链表
                    lists[idx++] = mergeTwoLists(lists[i], lists[i + 1]);
                }
            }
            // 重置新的一轮需要合并的链表数量
            k = idx;
        }
        // 合并至仅剩一条链表,结束while循环
        return lists[0];
    }

	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;
			}
	    }

}

执行用时:2 ms, 在所有 Java 提交中击败了94.90%的用户
内存消耗:42 MB, 在所有 Java 提交中击败了45.59%的用户

分治合并——递归

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        return merge(lists, 0, lists.length - 1);
    }


    private ListNode merge(ListNode[] lists, int lo, int hi) {
    	// base case
        if (lo == hi) {
            return lists[lo];
        }
        int mid = lo + (hi - lo) / 2;
        // 递归合并前后两部分
        ListNode l1 = merge(lists, lo, mid);
        ListNode l2 = merge(lists, mid + 1, hi);
        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;
		}
    }
}

执行用时:4 ms, 在所有 Java 提交中击败了69.13%的用户
内存消耗:42.7 MB, 在所有 Java 提交中击败了32.35%的用户

思路2:K 指针

使用K 个指针分别指向 K 条链表。

基础方案

每次 比较 K个指针求 min, 然后选择min节点连接到新链后中,这部分时间复杂度为O(K),一共N个节点,因此时间复杂度:O(NK)

class Solution {
    public ListNode mergeKLists(ListNode[] lists) { 
        int k = lists.length;
        ListNode newHead = new ListNode(0);
        ListNode tail = newHead;
        while (true) {
            ListNode minNode = null;
            int minPointer = -1;
            // 遍历所有的头节点,找到最小的节点
            for (int i = 0; i < k; i++) {
                if (lists[i] == null) {
                    continue;
                }
                if (minNode == null || lists[i].val < minNode.val) {
                    minNode = lists[i];
                    minPointer = i;
                }
            }
            // 没有剩余节点,跳出循环
            if (minPointer == -1) {
                break;
            }

            // 将最小节点连接到新链表后
            **tail.next = minNode;
            tail = tail.next;
            // 最小节点所在链表的表头后移**
            lists[minPointer] = lists[minPointer].next;
        }
        return newHead.next;
    }
}

优化方案:优先队列

在选取最小元素的时候,我们可以用优先队列(利用的是小顶堆来实现)来优化这个过程。

在这里插入图片描述

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
    	// 初始化一个优先队列,并指定为小顶堆方式
        Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);

        // 将所有链表的头节点构造优先队列
        for (ListNode node: lists) {
            if (node != null) {
                pq.offer(node);
            }
        }

        ListNode newHead = new ListNode(0);
        ListNode tail = newHead;
        while (!pq.isEmpty()) {
        	// 出队节点一定是最小的
            ListNode minNode = pq.poll();
            // 连接到新链表后
            tail.next = minNode;
            tail = minNode;
            // 最小节点所在链表表头后移(重新入队)
            if (minNode.next != null) {
                pq.offer(minNode.next);
            }
        }

        return newHead.next;
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(Java)leetcode-23 Merge k Sorted Lists(合并K个排序链表) 的相关文章

随机推荐

  • it项目管理(6)

    1 教材练习题6 a b 路径1 A B E H K 长度 2 2 2 2 2 10 天 路径2 A B E I J K 长度 2 2 2 5 1 2 14 天 路径3 A C F H K 长度 2 3 3 2 2 12 天 路径4 A C
  • 如何用Python获取网页指定内容

    文章目录 1 抓取网页源代码 2 抓取一个网页源代码中的某标签内容 3 抓取多个网页子标签的内容 Python用做数据处理还是相当不错的 如果你想要做爬虫 Python是很好的选择 它有很多已经写好的类包 只要调用 即可完成很多复杂的功能
  • 服务器性能问题排查

    服务器性能问题一般有两种 高内存占用 高CPU占用 比如应用程序高内存占用 可能是因为文件读写 频繁的IO 内存频繁GC 进一步占用了内存和CPU 比如应用程序高CPU占用 可能是因为大任务计算 死循环 卡死 不断超时或者重试 所以需要具体
  • 基于SpringBoot开发的疫情信息管理系统

    文章目录 项目介绍 主要功能截图 部分代码展示 设计总结 项目获取方式 作者主页 超级无敌暴龙战士塔塔开 简介 Java领域优质创作者 简历模板 学习资料 面试题库 关注我 都给你 文末获取源码联系 项目介绍 疫情信息管理系统 java项目
  • 学习网络编程No.6【将服务器日志和守护进程化】

    引言 北京时间 2023 9 1 21 15 下午刚更新完博客 同理再接再厉 这样整天不需要干什么 除了玩手机的日子不多了 马上就要开学 每天需要签到签退的日子就要来临 烦躁 照我预料下学期我们学校应该会开一门Java的专业课 现在这种线下
  • ESP32-CAM摄像头开发

    1 硬件接线 参考博客 https blog csdn net wangyilong153 article details 124366728 ops request misc 257B 2522request 255Fid 2522 25
  • ply文件格式详细说明

    典型的 PLY 文件结构 头部 顶点列表 面片列表 其他元素列表 头部是一系列以回车结尾的文本行 用来描述文件的剩余部分 头部包含一个对每个元素类型的描述 包括元素名 如 边 这个元素在工程里有多少 以及一 个与这个元素关联的不同属性的列表
  • 假设检验2

    为研究东 中 西部各省市规模以上的企业发展状况 我们收集了各城市企业的主要经济指标 包括 总资产贡献率 资产负债率 流动资产周转次数 工业成本费用利润率 产品销售率 我们用变量 类别 定义了各类城市 其中1为东部城市 2为中部城市 3为西部
  • IV转换电路 IV放大 跨阻放大器 光电信号放大器 原理图及PCB设计分析

    IV转换电路 IV放大 跨阻放大器 光电信号放大器 原理图及PCB设计分析 目录 IV转换电路 IV放大 跨阻放大器 光电信号放大器 原理图及PCB设计分析 基本原理 芯片选型 原理图 3D PCB 具体讲解 模块原理图 PDF 原理图库
  • C# winform流程图项目(功能完整,中文注释,附下载链接)绘制各种流程图形,保存,步骤记录,删除,连接断开,直线折线,属性调节

    C winform流程图项目 功能完整 中文注释 附下载链接 绘制各种流程图形 保存 步骤记录 删除 连接断开 直线折线 属性调节 点我下载项目源码 主要功能如下 1 鼠标点击工具箱后在画布点击拖出图形 2 选中直线节点靠近图形节点自动连接
  • 14年macmini装双硬盘_廉颇老矣,还能战否?2014 Mac Mini Late 加装HP EX920固态硬盘

    廉颇老矣 还能战否 2014 Mac Mini Late 加装HP EX920固态硬盘 2019 03 13 13 49 17 15点赞 53收藏 25评论 小编注 此篇文章来自即可瓜分10万金币 周边好礼达标就有 邀新任务奖励无上限 点击
  • (七)Mybatis当中#{}和${}的区别详解

    这篇文章主要讲述Mybatis当中 和 的区别 对大家的学习或者工作具有一定的参考学习价值 需要的朋友们下面随着小编来一起学习学习吧 和 的区别 key 获取参数的值 预编译到SQL中 安全 key 获取参数的值 拼接到SQL中 有SQL注
  • 【FPGA】十三、Vivado MIG IP核实现DDR3控制器(1)

    文章目录 前言 一 DDR3基础知识 二 MIG IP核的配置 三 DDR3 IP核用户端接口时序 1 DDR3 IP核接口说明 2 DDR3 IP核读写时序 写命令时序 写数据时序 读数据时序 总结 前言 我们在进行FPGA开发应用当中
  • 利用Opencv提供的imencode和imdecode进行图像视频传输(发送端支持Linux和Windows双系统)

    关于网络图像传输 网上大多数都是基于像素访问进行传输 传输的大小是图像的分辨率以及他的通道数 一般普通摄像头拍摄到图像大小的分辨率是640480 也就是说单通道灰度图像 一次要传输的数据量大小是640480 307200个字节 如果是彩色3
  • k8s服务无法访问

    无法访问k8s服务问题分析过程 1 查看pod是否正常 2 查看service是否正常 3 查看endpoints是否绑定 4 检查配置文件 从过程3可以看出问题出在endpoints的绑定上面 通过仔细检测配置文件发现是pod的配置中ap
  • STM32HAL库-移植mbedtls开源库示例(一)

    目录 概述 一 使用方法 二 STM32CubeMx配置 三 Examples 四 运行结果 五 总结 概述 本篇文章介绍如何使用STM32HAL库 移植mbedtls开源库支持mqtt证书加密示例 GitHub https github
  • 剑指 Offer 43. 1~n整数中1出现的次数 思路整理

    题目描述 输入一个整数 n 求1 n这n个整数的十进制表示中1出现的次数 例如 输入12 1 12这些整数中包含1 的数字有1 10 11和12 1一共出现了5次 原题链接 https leetcode cn com problems 1n
  • 【腾讯云 Cloud Studio 实战训练营】用于编写、运行和调试代码的云 IDE泰裤辣

    文章目录 一 引言 二 什么是腾讯云 Cloud Studio 三 Cloud Studio优点和功能 四 Cloud Studio初体验 注册篇 五 Cloud Studio实战演练 实战篇 1 初始化工作空间 2 安装 antd mob
  • kaggle资源

    2019 03 07 这里记录几个认为比较好的kaggle kernel 有些是数据分析 有些是针对算法 1 COMPREHENSIVE DATA EXPLORATION WITH PYTHON 这个kernel通过对变量分析 他的数据集都
  • (Java)leetcode-23 Merge k Sorted Lists(合并K个排序链表)

    题目描述 合并 k 个排序链表 返回合并后的排序链表 请分析和描述算法的复杂度 示例 输入 1 gt 4 gt 5 1 gt 3 gt 4 2 gt 6 输出 1 gt 1 gt 2 gt 3 gt 4 gt 4 gt 5 gt 6 思路1