经典算法之链表篇(二)

目录

一:重排链表(LeetCode.143)

 二:删除链表的节点(LCR 136. 删除链表的节点)

三:K个一组反转链表(LeetCode.25)


 有关经典算法链表的第一篇内容,可以查看我的上一篇内容:经典算法之链表篇(一)

一:重排链表(LeetCode.143)

问题描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln-1 → Ln请将其重新排列后变为:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例

输入: head = [1,2,3,4]
输出: [1,4,2,3] 

示例2

输入: head = [1,2,3,4,5]
输出: [1,5,2,4,3] 

解题思路:

  • 找到链表中点:使用快慢指针找到链表的中点。快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针指向链表中点。

  • 反转后半部分链表:从中点处将链表分为两部分,将后半部分链表进行反转。

  • 合并链表:将前半部分链表和反转后的后半部分链表依次交替合并,即可得到重新排列后的链表。

  • 处理边界情况:需要注意链表为空或只有一个节点的情况,直接返回即可
    图示:
    第一步:

    第二步:

    第三步:

    第四步:

代码实现

class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}class Solution {public void reorderList(ListNode head) {if (head == null || head.next == null) return;// 找到链表的中间节点ListNode slow = head;ListNode fast = head;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}// 反转后半部分链表ListNode pre = null;ListNode cur = slow.next;slow.next = null; // 断开前后两部分链表while (cur != null) {ListNode next = cur.next;cur.next = pre;pre = cur;cur = next;}// 合并两部分链表ListNode p1 = head;ListNode p2 = pre;while (p2 != null) {ListNode tmp1 = p1.next;ListNode tmp2 = p2.next;p1.next = p2;p2.next = tmp1;p1 = tmp1;p2 = tmp2;}}
}public class Main {public static void main(String[] args) {int[] nums = {1, 2, 3, 4, 5};ListNode head = createList(nums);// 重排链表Solution solution = new Solution();solution.reorderList(head);// 输出重排后的链表printList(head);}// 创建链表public static ListNode createList(int[] nums) {if (nums.length == 0) return null;ListNode head = new ListNode(nums[0]);ListNode cur = head;for (int i = 1; i < nums.length; ++i) {cur.next = new ListNode(nums[i]);cur = cur.next;}return head;}// 输出链表public static void printList(ListNode head) {ListNode cur = head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}
}

 二:删除链表的节点(LCR 136. 删除链表的节点)

 问题描述

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

示例

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

解题思路

  • 判断链表是否为空,若为空直接返回 nullptr。

  • 创建一个虚拟头节点 dummy,并将其指向头节点 head,这样做是为了方便处理头节点的删除操作。

  • 初始化两个指针 precur,分别指向虚拟头节点和头节点。

  • 遍历链表,查找要删除的节点:

  • 如果当前节点的值等于要删除的值 val,则将前一个节点 prenext 指针指向当前节点的下一个节点 cur->next,即完成删除操作。

  • 否则,更新 precur 指针,继续遍历链表。

  • 完成遍历后,更新头节点 head 为虚拟头节点的下一个节点 dummy->next,即删除可能存在的头节点。

  • 释放虚拟头节点的内存,避免内存泄漏。

  • 返回更新后的头节点 head
    图示:
    第一步:

    第二步:

    第三步:遍历链表,查找要删除的节点
    第四步:

代码演示

class ListNode {int val;ListNode next;ListNode(int x) {val = x;}
}class Solution {public ListNode deleteNode(ListNode head, int val) {if (head == null) return null; // 如果链表为空,直接返回 null// 创建一个虚拟头节点,方便处理头节点的删除ListNode dummy = new ListNode(0);dummy.next = head;ListNode pre = dummy; // 前一个节点指针ListNode cur = head; // 当前节点指针while (cur != null) {if (cur.val == val) { // 如果当前节点的值等于要删除的值pre.next = cur.next; // 将前一个节点的指针指向当前节点的下一个节点break; // 找到并删除节点后退出循环}pre = cur; // 更新前一个节点指针cur = cur.next; // 更新当前节点指针}head = dummy.next; // 更新头节点return head; // 返回头节点}
}public class Main {// 创建链表public static ListNode createList(int[] nums) {if (nums.length == 0) return null;ListNode head = new ListNode(nums[0]);ListNode cur = head;for (int i = 1; i < nums.length; ++i) {cur.next = new ListNode(nums[i]);cur = cur.next;}return head;}// 输出链表public static void printList(ListNode head) {ListNode cur = head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}public static void main(String[] args) {// 输入链表int[] nums = {4, 5, 1, 9};ListNode head = createList(nums);int val = 5;// 删除指定值的节点Solution solution = new Solution();head = solution.deleteNode(head, val);// 输出删除后的链表printList(head);}
}

三:K个一组反转链表(LeetCode.25)

问题描述

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例
 

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例二

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

解题思路

  • 创建一个虚拟头节点 dummy,将其 next 指向原链表的头节点 head,方便处理头部的特殊情况。

  • 使用 pre 指针来记录每个需要翻转的子链表的前一个节点。初始时,pre 指向虚拟头节点。

  • 在循环中,先找到需要翻转的子链表的起始节点 start 和结束节点 end。如果剩余节点不足 k 个,则结束循环。

  • 将当前子链表与下一个子链表断开,即将 end->next 置为 nullptr

  • 调用 reverse 函数翻转当前子链表,并将翻转后的子链表连接到前一个子链表的末尾,即将 pre->next 指向翻转后的子链表的头节点。

  • 将翻转后的子链表的末尾与下一个子链表的开头连接,即将 start->next 指向下一个需要翻转的子链表的第一个节点。

  • 更新 pre 指向下一个需要翻转的子链表的前一个节点。

  • 循环直到所有子链表都被翻转。
    图示:
    第一步:

    第二步:

    第三步:

    第四步:

    第五步:

    第六步:

    第七步:

 代码演示

class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dummy = new ListNode(0); // 创建一个虚拟头节点,方便处理头部的特殊情况dummy.next = head;ListNode prev = dummy; // prev 指向每个需要翻转的子链表的前一个节点while (true) {ListNode start = prev.next; // start 指向当前需要翻转的子链表的第一个节点ListNode end = prev; // end 指向当前需要翻转的子链表的最后一个节点for (int i = 0; i < k && end != null; ++i) {end = end.next; // 找到当前需要翻转的子链表的最后一个节点}if (end == null) {break; // 如果剩余节点不足 k 个,结束循环}ListNode nextGroup = end.next; // nextGroup 指向下一个需要翻转的子链表的第一个节点end.next = null; // 将当前子链表与下一个子链表断开prev.next = reverse(start); // 翻转当前子链表,并将翻转后的子链表连接到前一个子链表的末尾start.next = nextGroup; // 将翻转后的子链表的末尾与下一个子链表的开头连接prev = start; // 更新 prev 指向下一个需要翻转的子链表的前一个节点}return dummy.next; // 返回虚拟头节点的下一个节点作为翻转后的链表的头节点}private ListNode reverse(ListNode head) {ListNode prev = null;ListNode curr = head;while (curr != null) {ListNode next = curr.next;curr.next = prev;prev = curr;curr = next;}return prev;}
}public class Main {public static void main(String[] args) {ListNode head = new ListNode(1);head.next = new ListNode(2);head.next.next = new ListNode(3);head.next.next.next = new ListNode(4);head.next.next.next.next = new ListNode(5);Solution solution = new Solution();ListNode newHead = solution.reverseKGroup(head, 2);printList(newHead); // 输出:2 1 4 3 5}private static void printList(ListNode head) {ListNode curr = head;while (curr != null) {System.out.print(curr.val + " ");curr = curr.next;}System.out.println();}
}

总结

这三道链表题相比较于上篇的三道题难度有些增加,因此要多加注重理解。作者在写算法题的时候也借鉴了许多技术大佬的相关博客知识和力扣官方的解题思路,后续还会再写有关链表的经典算法题,大家可以持续关注!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/878232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESXi服务器无法安装Windows11:“不符合此版本的Windows所需最低系统要求“

目录 一、问题描述1.使用环境2.问题截图3.问题解析 二、解决方法Ⅰ1.按 ShiftF10 弹出命令提示符2.在弹出的Dos框中输入regedit&#xff0c;回车&#xff0c;进入注册表。3.打开HKEY_LOCAL_MACHINE\SYSTEM\Setup&#xff0c;并新建 LabConfig 的项&#xff0c;在 LabConfig 下创…

使用预训练的 ONNX 格式的 YOLOv8n 模型进行目标检测,并在图像上绘制检测结果

目录 __init__方法&#xff1a; pre_process方法&#xff1a; run方法&#xff1a; filter_boxes方法&#xff1a; view_img方法&#xff1a; __init__方法&#xff1a; 初始化类的实例时&#xff0c;创建一个onnxruntime的推理会话&#xff0c;加载名为yolov8n.onnx的模型…

C#开发基础之100个常用的C#正则表达式

前言 正则表达式是处理字符串的强大工具&#xff0c;特别是在文本搜索、替换和验证中。本文将100个常用的C#正则表达式进行分类&#xff0c;以帮助我们更快速地找到适合的正则表达式解决方案。 1. 基础匹配 这些正则表达式用于匹配一些基本的字符或字符串模式。 匹配任意字…

利用 Pytest Cache Fixture 实现中间数据缓存

接口自动化过程中&#xff0c;经常会遇到这样一些场景&#xff0c;“请求2需要用到请求1响应的数据”&#xff0c;常见的做法&#xff0c;进行用例依赖或者将请求1的响应结果写入一个文件&#xff0c;用到的时候读取文件。 当然这都不是这篇文章的重点&#xff0c;本片文章主要…

macos MacPort 包管理工具安装和使用

在macos v10.15版本中, xz, python等软件无法使用brew安装, 原因是brew对于旧版本的macos不再支持, 但是我们可以使用另外一个macos下的包管理工具来安装brew无法安装的软件, macport 是一个和brew类似的macos下的一个非常优秀的软件包安装管理工具. MacPort安装前提条件 安…

假期学习--对象底层结构和继承链

OC本质底层实现转化其实都是C/C代码。 OC对象的本质就是结构体。 NSObject底层是struct objc_object结构体 &#xff1b;struct objc_class : objc_object { …省略无关代码 // Class ISA; //ISA(从objc_object继承过来的) Class superclass; //指向其父类 cache_t cache…

旅游行业怎么利用C#接口发送短信

旅游企业一般拥有众多的分支机构&#xff0c;同时各地分支机构又有众多下属分散在当地各区的旅游营业报名点&#xff0c;以前传统的解决方案是采用专线、MODEM拔号等方式&#xff0c;专线的成本很高&#xff0c;MODEM拔号更费时&#xff0c;且长途拔号互联成本在多点情况下费用…

微前端集成优化:让所有子应用体积更小,加载更快!

简介 随着前端的日益发展&#xff0c;微前端架构越来越受到青睐。它通过将前端应用拆分为多个独立的子应用&#xff0c;每个子应用可以独立开发、部署和运行&#xff0c;从而提升了开发效率和团队协作。目前主流的微前端方案应该是qiankun了。 以笔者公司为例&#xff0c;采用…

基于SpringBoot的在线答疑系统

你好呀&#xff0c;我是计算机专业毕业生&#xff0c;专注于在线教育平台的开发与实现。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术 Spring Boot框架 工具&#xff1a;IntelliJ IDEA、Navicat、Maven、Tomcat 系统展示 首页 个人中心…

scrapy--图片管道-ImagesPipeline

免责声明:本文仅做演示与分享~ 目录 介绍 ImagesPipeline pipelines.py items.py zz.py settings.py 介绍 scrapy 还提供了处理图片、视频、音频等媒体文件的插件&#xff0c;如&#xff1a; - scrapy-images&#xff1a;用于下载和处理图片 - scrapy-video&#xff1…

责任链设计模式详解

责任链设计模式详解 一、定义 责任链设计模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;它允许多个对象有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合。这种模式将这些对象连接成一条链&#xff0c;并沿着…

提前还房贷结果失败了该怎么办?需要注意哪些?怎么做更顺利?

提前还房贷结果失败了&#xff0c;该怎么办&#xff1f; 1. 满足条件再申请&#xff1a;部分银行对提前还款设有一定的条件和限制&#xff0c;例如需要提前预约&#xff0c;对已还款时间和还款金额也有具体的要求。如果借款人未能满足这些条件&#xff0c;提前还款的申请可能会…

【精选】计算机毕业设计之:基于springboot超市进销存系统

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Stable Diffusion AI绘画工具的安装与配置(MAC用户)

AI绘画的热潮席卷了整个创意行业&#xff0c;Stable Diffusion作为其中的翘楚&#xff0c;让艺术创作变得前所未有的简单。然而&#xff0c;对于使用Mac电脑用户来说&#xff0c;安装和配置Stable Diffusion可能显得有些棘手。别担心&#xff0c;这份详细的教程将手把手教你如何…

【Material-UI】Select 组件中的 `Auto width`、`Small Size` 和 `Other Props` 详解

文章目录 一、Select 组件概述1. 组件介绍2. Select 组件的基本结构 二、Auto width 属性详解1. Auto width 的作用2. Auto width 属性的基本用法3. Auto width 的实际应用场景 三、Small Size 属性详解1. Small Size 的作用2. Small Size 属性的基本用法3. Small Size 的实际应…

Windows怎么让防火墙开放端口

开放端口的方法 先从控制面板,进入到Windows Defender防火墙 点击高级设置,点击入站规则 点击右边的新建规则,点击端口,点击下一步 选择协议类型和端口号点击下一步即可 查看是否开放端口成功的方法: 进入任务管

【rk3588】环境搭建及系统编译

开发板&#xff1a;ROC-RK3588S-PC 官方链接&#xff1a;Welcome to ROC-RK3588S-PC Manual — Firefly Wiki (t-firefly.com) 串口调试配置 一、产品介绍 — Firefly Wiki (t-firefly.com)&#xff0c;可以按照官方链接的说明在个人PC上使用串口。这个串口会输出rk3588的日…

【Python机器学习】NLP词频背后的含义——从词频到主题得分

目录 TF-IDF向量及词形归并 主题向量 一个思想实验 一个主题评分算法 一个LDA分类器 LDiA TF-IDF向量&#xff08;词项频率—逆文档频率向量&#xff09;可以帮助我们估算词在文本块中的重要度&#xff0c;我们使用TF-IDF向量和矩阵可以表明每个词对于文档集合中的一小段…

计算机视觉编程 1(图片处理)

目录 灰色度 缩略图 拷贝粘贴区域 调整图像尺寸 旋转图像45 画图线、描点 灰色度 灰度是指图像中每个像素的亮度值&#xff0c;用来描述图像中各个像素的明暗程度。在计算机视觉中&#xff0c;灰度可以通过以下方式来计算&#xff1a; 1. 平均值法&#xff1a;将图像中每…

Java基础——自学习使用(泛型)

一、泛型的定义 泛型的本质是参数化类型&#xff0c;也就是所操作的数据类型被指定为一个参数。 泛型泛指一切类型&#xff0c;能够代表一切类型&#xff0c;是一种在编程中广泛使用的概念&#xff0c;特别是在面向对象编程中。它允许在编写代码时使用类型参数&#xff0c;这些…