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

坚持刷题|合并有序链表

文章目录

  • 题目
  • 思考
  • 代码实现
    • 迭代
    • 递归
  • 扩展
    • 实现k个有序链表合并
      • 方法一
      • 方法二
    • PriorityQueue
      • 基本操作
      • Java示例
      • 注意事项

Hello,大家好,我是阿月。坚持刷题,老年痴呆追不上我,消失了一段时间,我又回来刷题啦,今天先刷个简单的:合并有序链表

题目

21. 合并两个有序链表
在这里插入图片描述

思考

合并有序链表这一算法问题主要考察以下几个关键点

  1. 链表操作:理解链表数据结构以及如何遍历、比较、连接链表节点。
  2. 边界条件处理:包括处理一个或两个链表为空的情况,以及其中一个链表先到达末尾的场景。
  3. 递归与迭代:可以选择递归或迭代的方法来解决问题,每种方法都有其优缺点,需要理解何时使用哪一种。
  4. 效率考量:优化算法的时间复杂度和空间复杂度,例如使用优先队列可以达到较好的时间性能。
  5. 代码整洁性:写出清晰、可读性强的代码,避免不必要的复杂性和冗余。
  6. 错误处理:在实际编码中,考虑到可能出现的异常情况,比如处理null指针异常。
  7. 算法设计:理解不同算法策略,如两两合并法、使用辅助数据结构等。
  8. 优化技巧:了解如何在不增加额外空间复杂度的情况下解决问题,例如原地修改链表而非创建新的链表。
  9. 测试用例:编写全面的测试用例验证代码的正确性,包括极端情况和常见情况。
  10. 性能分析:能够分析和解释算法的时间和空间复杂度,理解为什么某种方法优于其他方法。

代码实现

合并两个有序链表是一个常见的链表操作,可以通过迭代或递归的方式实现。

迭代

public class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode current = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {current.next = l1;l1 = l1.next;} else {current.next = l2;l2 = l2.next;}current = current.next;}current.next = l1 == null ? l2 : l1;return dummy.next;}
}

在这段代码中,我们首先创建一个虚拟头节点dummy和一个指针cur指向dummy。然后我们遍历两个链表,每次都把较小的节点接到cur的后面,并移动cur和较小节点所在链表的头指针。当一个链表遍历完后,我们就直接把另一个链表接到cur的后面。最后返回dummy.next就是合并后的链表。

递归

使用递归的方式来合并两个有序链表是一种直观且简洁的方法。以下是使用Java实现的示例代码:

public class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {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;}}
}

在上述代码中,mergeTwoLists方法接受两个链表的头节点l1l2作为参数。该方法首先检查两个链表是否为空。如果其中一个链表为空,那么就直接返回另一个链表,因为这意味着另一个链表已经是合并后的结果。

如果两个链表都不为空,那么就比较两个链表头节点的值。如果l1的值小于l2的值,那么就将l1next字段设置为l1.nextl2合并的结果,然后返回l1。反之亦然,如果l2的值小于等于l1的值,那么就将l2next字段设置为l1l2.next合并的结果,然后返回l2

这种方法利用了递归的特性,逐步将大问题分解成小问题,直到问题简单到可以直接解决(即一个链表为空)。由于递归的深度最多为链表的长度,因此这种方法的时间复杂度为O(n),其中n为两个链表中节点的总数。

扩展

实现k个有序链表合并

方法一

我们可以使用分治的策略,将K个链表两两合并,直到剩下最后一个链表。这就是所谓的"两两合并"策略。以下是使用Java实现的代码:

public class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public ListNode mergeKLists(ListNode[] lists) {int amount = lists.length;if(amount==0){return null;}int interval = 1;while (interval < amount) {for (int i = 0; i < amount - interval; i += interval * 2) {lists[i] = mergeTwoLists(lists[i], lists[i + interval]);}interval *= 2;}return lists[0];}private ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode current = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {current.next = l1;l1 = l1.next;} else {current.next = l2;l2 = l2.next;}current = current.next;}current.next = l1 == null ? l2 : l1;return dummy.next;}
}

在这个代码中,我们首先定义了一个mergeTwoLists方法用于合并两个有序链表。然后在mergeKLists方法中,我们使用了"两两合并"的策略来合并K个有序链表。
这种方法的时间复杂度为O(N log K),其中N是所有链表中节点的总数,K是链表的数量。空间复杂度为O(1)。

方法二

在Java中,我们还可以使用 PriorityQueue 来实现。

import java.util.PriorityQueue;public class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public ListNode mergeKLists(ListNode[] lists) {PriorityQueue<ListNode> queue = new PriorityQueue<>((a, b) -> a.val - b.val);ListNode dummy = new ListNode(0);ListNode tail = dummy;for (ListNode node : lists) {if (node != null) {queue.add(node);}}while (!queue.isEmpty()) {ListNode node = queue.poll();tail.next = node;tail = tail.next;if (node.next != null) {queue.add(node.next);}}return dummy.next;}
}

在这个解决方案中,我们首先创建一个虚拟头节点和一个尾节点,然后遍历所有的链表,将每个链表的头节点放入优先队列中。然后我们开始循环处理优先队列,每次从队列中取出最小的元素,将其添加到结果链表中,然后将其下一个节点(如果存在)放入队列中。最后返回虚拟头节点的下一个节点,即为合并后的链表的头节点。

  • 注意,Java中的 PriorityQueue 默认是按照自然顺序排序的,所以我们需要提供一个比较器来确保按照节点的值进行排序。PriorityQueue是Java集合框架的一部分,它是一个基于优先堆的无界优先队列。此队列按照元素的自然排序进行排序,或者根据构造队列时提供的Comparator进行排序,具体取决于使用的构造方法。

PriorityQueue

最小堆是一种特殊的完全二叉树结构,其中每个父节点的值都小于或等于其子节点的值。这种结构保证了堆的根节点始终是最小的元素,从而使得查找最小元素的操作非常快速。最小堆在Java中可以通过java.util.PriorityQueue类来实现,因为这个类内部就是使用数组实现的最小堆。

基本操作

  1. 插入元素 (offeradd):将一个元素添加到堆中,同时保持堆的性质不变,但是当队列已满时,add会抛出异常,而offer则返回false。
  2. 删除最小元素 (poll):从堆中移除并返回最小的元素。
  3. 获取最小元素 (peek):返回但不移除最小的元素。
  4. 移除并返回队列头部的元素(remove):如果队列为空,则抛出NoSuchElementException异常。

Java示例

首先,我们创建一个PriorityQueue实例,这将作为我们的最小堆:

import java.util.PriorityQueue;public class MinHeapExample {public static void main(String[] args) {// 创建一个最小堆PriorityQueue<Integer> minHeap = new PriorityQueue<>();// 插入元素minHeap.offer(5);minHeap.offer(1);minHeap.offer(3);minHeap.offer(4);System.out.println("堆中的元素:" + minHeap);// 获取最小元素Integer smallest = minHeap.peek();System.out.println("最小元素是:" + smallest);// 删除最小元素Integer removed = minHeap.poll();System.out.println("删除的最小元素:" + removed);System.out.println("删除后堆中的元素:" + minHeap);}
}

运行上述代码,你将看到如下输出:

堆中的元素:[1, 4, 3, 5]
最小元素是:1
删除的最小元素:1
删除后堆中的元素:[3, 4, 5]

注意事项

  • PriorityQueue默认情况下实现的是最小堆,如果要实现最大堆,可以自定义比较器Comparator
  • PriorityQueue不允许插入null元素,否则会抛出NullPointerException
  • PriorityQueue没有固定大小,它可以动态调整大小。
  • 通过上面的示例,可以看到PriorityQueue如何在内部自动维护堆的性质,使得最小元素始终位于堆的顶部,方便我们进行高效的查找和删除操作。
  • PriorityQueue的一个重要特性是它的元素必须是可比较的,也就是说,它们必须实现了Comparable接口,或者在创建PriorityQueue时提供了一个Comparator。

例如,下面的代码创建了一个按照字符串长度排序的PriorityQueue

PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>() {public int compare(String s1, String s2) {return s1.length() - s2.length();}
});

在这个例子中,我们提供了一个Comparator,它将字符串按照长度进行排序。

相关文章:

坚持刷题|合并有序链表

文章目录 题目思考代码实现迭代递归 扩展实现k个有序链表合并方法一方法二 PriorityQueue基本操作Java示例注意事项 Hello&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;消失了一段时间&#xff0c;我又回来刷题啦&#xff0c;今天…...

SPI协议——对外部SPI Flash操作

目录 1. W25Q32JVSSIQ背景知识 1.1 64个可擦除块 1.2 1024个扇区&#xff08;每个块有16个扇区&#xff09; 1.3 页 1. W25Q32JVSSIQ背景知识 W25Q32JV阵列被组织成16,384个可编程页&#xff0c;每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除&…...

kotlin类型检测与类型转换

一、is与!is操作符 1、使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型。 fun main() {var a "1"if(a is String) {println("a是字符串类型:${a.length}")}// 或val b a is Stringprintln(b) } 二、"不安全的"转换操作符…...

【JDBC】Oracle数据库连接问题记录

Failed to load driver class oracle.jdbc.driver.OracleDriver in either of HikariConfig class oracle驱动包未正确加载&#xff0c;可以先尝试使用下面方式加载检查类是否存在&#xff0c;如果不存在需要手动下载odbc包 try {Class.forName("oracle.jdbc.driver.Ora…...

leetcode45 跳跃游戏II

题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1]…...

【数学】什么是方法矩估计?和最大似然估计是什么关系?

背景 方法矩估计&#xff08;Method of Moments Estimation&#xff09;和最大似然估计&#xff08;Maximum Likelihood Estimation, MLE&#xff09;是两种常用的参数估计方法。方法矩估计基于样本矩与总体矩的关系&#xff0c;通过样本数据计算样本矩来估计总体参数。最大似…...

C++初学者指南第一步---10.内存(基础)

C初学者指南第一步—10.内存&#xff08;基础&#xff09; 文章目录 C初学者指南第一步---10.内存&#xff08;基础&#xff09;1.内存模型1.1 纸上谈兵&#xff1a;C的抽象内存模型1.2 实践&#xff1a;内存的实际处理 2. 自动存储3.动态存储&#xff1a;std::vector3.1 动态内…...

扩散模型详细推导过程——编码与解码

符号表 符号含义 x ( i ) z 0 ( i ) \boldsymbol{x}^{(i)}\boldsymbol{z}_0^{(i)} x(i)z0(i)​第 i i i个训练数据&#xff0c;其为长度为 d d d的向量 z t ( i ) \boldsymbol{z}_t^{(i)} zt(i)​第 i i i个训练数据在第 t t t时刻的加噪版本 ϵ t ( i ) \boldsymbol{\epsilo…...

js如何实现开屏弹窗

开屏弹窗是什么&#xff0c;其实就是第一次登录后进入页面给你的一种公告提示&#xff0c;此后再回到当前这个页面时弹窗是不会再出现的。也就是说这个弹窗只会出现一次。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>…...

C#——文件读取Directory类详情

文件读取Directory类 Durectory提供了目录以及子目录进行创建移动和列举操作方法 Directory和Directorylnfo类(主要操作文件目录属性列如文件是否隐藏的 或者只读等这些属性) Directory对目录进行复制、移动、重命名、创建和删除等操作DirectoryInfo用于对目录属性执行操作 …...

Ruby on Rails Post项目设置网站初始界面

在构建了Ruby的Web服务器后&#xff0c;第三步就可以去掉框架的官方页面&#xff0c;设置自己的网页初始页了。 Linux系统安装Ruby语言-CSDN博客 、在Ubuntu中创建Ruby on Rails项目并搭建数据库-CSDN博客、 Ruby语言建立Web服务器-CSDN博客 了解Ruby onRails项目中的主要文件…...

03-QTWebEngine中使用qtvirtualkeyboard

qt提供了 virtualKeyboard 虚拟键盘模块&#xff0c;只需要在在main函数中最开始加入这样一句就可以了 qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); 但是在使用的时候遇到了一些问题&#xff1a; 1、中文输入的时候没有输入提示 Qvirt…...

leetcode3无重复字符的最长字串(重点讲滑动窗口)

本文主要讲解无重复字符的最长字串的要点与细节&#xff0c;根据步骤一步步走更方便理解 c与java代码如下&#xff0c;末尾 具体要点&#xff1a; 1. 区分一下子串和子序列 子串&#xff1a;要求元素在母串中是连续地出现 子序列&#xff1a;不要求连续 2. 题目中有两个核心…...

Gobject tutorial 八

The GObject base class Object memory management Gobject的内存管理相关的API很复杂&#xff0c;但其目标是提供一个基于引用计数的灵活的内存管理模式。 下面我们来介绍一下&#xff0c;与管理引用计数相关的函数。 Reference Count 函数g_object_ref和g_object_unref的…...

DDMA信号处理以及数据处理的流程---cfar检测

Hello,大家好,我是Xiaojie,好久不见,欢迎大家能够和Xiaojie一起学习毫米波雷达知识,Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程,本系列文章将从目标生成、信号仿真、测距、测速、cfar检测、测角、目标聚类、目标跟踪这几个模块逐步介绍,这个系列的…...

【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f4d5;引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 &#x1f916;决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…...

Zookeeper 集群节点故障剔除、切换、恢复原理

Zookeeper 集群节点故障剔除、切换、恢复原理 zookeeper 集群节点故障时,如何剔除节点,如果为领导节点如何处理,如何进行故障恢 复的,实现原理? 在 Zookeeper 集群中,当节点故障时,集群需要自动剔除故障节点并进行故障恢复,确保集群的高 可用性和一致性。具体来说,…...

解决帝国cms栏目管理拼音乱码的问题

帝国CMS7.5版本utf-8版网站后台增加栏目生成乱码的问题怎么解决 1、需要改一个函数&#xff0c;并且增加一个处理文件&#xff0c;方法如下&#xff1a; 修改e/class/connect.php文件&#xff0c;找到ReturnPinyinFun函数&#xff0c;如未修改文件在4533-4547行&#xff0c;将…...

Git快速入门

一 快速使用 1.1 初始化 什么是版本库呢&#xff1f;版本库又名仓库&#xff0c;可以简单理解成一个目录&#xff0c;这个目录里面的所有文件都可以被Git管理起来&#xff0c;每个文件的修改、删除&#xff0c;Git都能跟踪&#xff0c;以便任何时刻都可以追踪历史&#xff0…...

【18.0】JavaScript---事件案例

【18.0】JavaScript—事件案例 【一】开关灯事件 【介绍】设置一个按钮&#xff0c;按下按钮触发事件&#xff0c;来回切换圆形图片的颜色 【分析】 图片设置&#xff1a;设置成圆形的图片背景颜色&#xff1a;设置红绿两个颜色&#xff0c;来回切换按钮设置&#xff1a;点击…...

3步解决Windows驱动臃肿难题:DriverStore Explorer让系统空间释放效率提升80%

3步解决Windows驱动臃肿难题&#xff1a;DriverStore Explorer让系统空间释放效率提升80% 【免费下载链接】DriverStoreExplorer Driver Store Explorer [RAPR] 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 诊断系统存储异常 "为什么我的C盘…...

Vue3最新版二维码生成避坑指南:从基础配置到企业级定制(附GitHub源码)

Vue3企业级二维码生成实战&#xff1a;从核心原理到性能优化 二维码作为连接物理世界与数字世界的桥梁&#xff0c;在现代Web应用中扮演着重要角色。本文将带您深入Vue3的二维码生成技术栈&#xff0c;不仅涵盖基础实现&#xff0c;更聚焦企业级应用中的高阶技巧与性能优化方案…...

杭州做生成式引擎优化的服务公司有哪些?

杭州做生成式引擎优化的服务公司有哪些&#xff1f; 一、行业背景&#xff1a;GEO已成为AI时代企业增长的核心基建 生成式引擎优化&#xff08;GEO&#xff0c;Generative Engine Optimization&#xff09;&#xff0c;是针对大语言模型的检索逻辑与回答规则&#xff0c;优化企…...

8_Harness驾驭工程实践:企业级落地与OpenAI案例解析

8_Harness驾驭工程实践&#xff1a;企业级落地与OpenAI案例解析 关键字&#xff1a; 企业级落地、OpenAI、Ryan Lopopolo、Codex、Harness Engineering、Citi Bank、Ancestry、Ulta Beauty、Agent-First开发、部署策略、自托管、成本优化、迁移路径、最佳实践、0行手写代码、百…...

viem ABI工具使用教程:编码、解码和类型推断全攻略

viem ABI工具使用教程&#xff1a;编码、解码和类型推断全攻略 【免费下载链接】viem TypeScript Interface for Ethereum 项目地址: https://gitcode.com/gh_mirrors/vi/viem viem是一个轻量级、可组合且类型安全的TypeScript以太坊接口工具库&#xff0c;其强大的ABI工…...

PCL点云凹包计算实战:从2D投影到3D建模的Alpha-Shape算法解析

1. Alpha-Shape算法&#xff1a;点云凹包计算的灵魂 第一次接触点云凹包计算时&#xff0c;我被这个看似简单实则精妙的问题难住了。传统凸包算法就像给点云套上一个紧绷的橡皮筋&#xff0c;而实际项目中我们经常需要保留物体表面的凹陷特征。这时候Alpha-Shape算法就派上了大…...

B端拓客号码核验:困局审视、技术革新与行业前行,氪迹科技法人股东号码核验系统,阶梯式价格

在B端拓客的全流程中&#xff0c;有效触达企业核心决策层是实现合作转化的关键&#xff0c;而法人、股东、董监高等群体的联系方式&#xff0c;則是搭建这一沟通链路的核心基础。号码核验作为拓客工作的前置核心环节&#xff0c;其筛选质量与效率&#xff0c;直接决定着拓客投入…...

三菱PLC与MCGS组态农田智能灌溉系统:后发送产品包括梯形图原理图、IO分配及组态画面解析

基于三菱PLC和MCGS组态农田智能灌溉系统 我们主要的后发送的产品有&#xff0c;带解释的梯形图接线图原理图图纸&#xff0c;io分配&#xff0c;组态画面上周刚把农田智能灌溉的项目收尾&#xff0c;把资料打包发给客户的时候&#xff0c;终于能瘫在椅子上喝杯冰可乐了。这个…...

好用还专业!盘点2026年备受推崇的一键生成论文工具

一天写完毕业论文在2026年已不再是天方夜谭。最新实测显示&#xff0c;一键生成论文工具正在颠覆传统写作方式&#xff0c;覆盖选题、文献、写作、降重、排版等核心场景&#xff0c;真正实现高效搞定论文&#xff0c;学生党必备神器。 一、全流程王者&#xff1a;一站式搞定论文…...

基于三相两电平逆变器的VSG并网系统:电压电流双闭环控制的仿真研究

VSG并网&#xff0c;基于三相两电平逆变器的虚拟同步机并网&#xff0c;电压电流双闭环控制 1.VSG 2.电压电流双闭环 3..提供相关参考文献 支持simulink2022以下版本&#xff0c;联系跟我说什么版本&#xff0c;我给转成你版本&#xff08;默认发2016b&#xff09;。最近在研究…...