【算法一则】反转链表

目录

  • 题目
  • 题解
  • 进阶

题目

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:输入:head = [5], left = 1, right = 1
输出:[5]
提示:链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

题解

看到这个题目之后我们第一时间可能想到用ArrayList包过所有的节点,然后通过ArrayList的下标更换来实现链表的反转。代码如下:

public ListNode reverseBetween1(ListNode head, int left, int right) {List<ListNode> list = new ArrayList<>();// 遍历链表while (head != null) {list.add(head);head = head.next;}// 交换for (int i = left - 1, j = right - 1; i <= j; i++, j--) {ListNode temp = list.get(i);list.set(i, list.get(j));list.set(j, temp);}// 重新连接for (int i = 0; i < list.size() - 1; i++) {list.get(i).next = list.get(i + 1);}// 最后一个节点的next置为nulllist.get(list.size() - 1).next = null;return list.get(0);
}

在这里插入图片描述

但这样明眼可见内存和时间都会很不理想,首先遍历了三次链表相当于O(3 * head.length), 空间复杂度也非常高。这个时候我们会想到能不能我的ArrayList只存需要反转那一部分呢,然后再跟不需要反转的链表合并连接下。

空间利用率提升

在这里插入图片描述

public ListNode reverseBetween(ListNode head, int left, int right) {ListNode startHeadPre = null; // left 节点的前一个节点ListNode endPre = null; // right 节点的后一个节点ListNode pre = new ListNode(); // 辅助节点,用于遍历链表pre.next = head; // 将辅助节点的下一个节点指向原始链表的头节点ListNode realHead = pre; // 记录原始链表的头节点List<ListNode> listNodes = new ArrayList<>(); // 用于存储需要反转的节点int index = 0; // 当前遍历的节点索引while (head != null && head.next != null) {if (index == left - 1) {startHeadPre = pre; // 记录 left 节点的前一个节点}if (startHeadPre == null) {index ++;pre = head;head = head.next;continue;}listNodes.add(head); // 将需要反转的节点加入到 listNodes 中if (index == right - 1) {endPre = head.next; // 记录 right 节点的后一个节点break;}index++;pre = head;head = head.next;}if (right - 1 == index) {listNodes.add(head); // 如果 right 节点是最后一个节点,则将其加入到 listNodes 中}ListNode reHead = new ListNode(); // 反转后的链表头节点ListNode reHeadTemp = reHead; // 用于遍历反转后的链表if (listNodes.size() > 0) {reHead.next = listNodes.get(listNodes.size() - 1); // 反转后的链表头节点指向 listNodes 的最后一个节点reHeadTemp = reHead.next;for (int i = listNodes.size() - 2; i >= 0; i--) {reHeadTemp.next = listNodes.get(i); // 逐个将节点加入到反转后的链表中reHeadTemp = reHeadTemp.next;}}if (left - 1 == 0) {realHead.next = reHead.next; // 如果 left 节点是原始链表的头节点,则更新原始链表的头节点为反转后的链表头节点}if (startHeadPre != null) {startHeadPre.next = reHead.next; // 将 left 节点的前一个节点的 next 指针指向反转后的链表头节点}reHeadTemp.next = endPre; // 将反转后的链表尾节点的 next 指针指向 right 节点的后一个节点return realHead.next; // 返回原始链表的头节点
}

这段代码实现了将链表中从 left 到 right 区间的节点进行反转。它使用了多个辅助节点和变量来记录反转过程中的节点位置。

通过遍历链表,找到 left 节点的前一个节点 startHeadPre 和 right 节点的后一个节点 endPre。同时,将需要反转的节点存储在 listNodes 中。

然后,创建反转后的链表头节点 reHead,并通过遍历 listNodes 将节点逐个加入到反转后的链表中。
最后,根据情况更新原始链表的头节点和连接反转后的链表。
这段代码的时间复杂度为 O(n),其中 n 是链表的长度。然而,它的实现方式相对复杂且效率较低。可以使用更简单和高效的方法来实现链表反转。

在这里插入图片描述
可以看到空间复杂度低了,但是代码异常复杂,很难理解(水平有限😂)。而且时间复杂度和空间复杂度也不是最优,能不能使用一趟扫描完成反转?

进阶

你可以使用一趟扫描完成反转吗?
在这里插入图片描述

public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummy = new ListNode(); // 创建虚拟头节点dummy.next = head; // 将虚拟头节点指向原始链表的头节点ListNode prev = dummy; // prev 指针用于定位到 left 的前一个节点// 移动 prev 到 left 的前一个节点for (int i = 0; i < left - 1; i++) {prev = prev.next;}ListNode start = prev.next; // 记录 left 节点ListNode end = start; // 记录 right 节点ListNode curr = start.next; // 当前节点// 反转链表节点for (int i = left; i < right; i++) {ListNode next = curr.next; // 保存当前节点的下一个节点curr.next = prev.next; // 将当前节点的 next 指针指向 prev 的 next 节点prev.next = curr; // 将 prev 的 next 指针指向当前节点start.next = next; // 将 start 的 next 指针指向 next 节点,保持链表连接curr = next; // 更新当前节点为下一个节点}return dummy.next; // 返回虚拟头节点的下一个节点,即为反转后的链表
}

在这里插入图片描述
这段代码实现了将链表中从 left 到 right 区间的节点进行反转。它使用了一个虚拟头节点 dummy,并通过 prev 指针定位到 left 的前一个节点。

在反转过程中,使用 start 记录 left 节点,end 记录 right 节点,curr 作为当前节点。通过遍历链表,将 curr 节点插入到 prev 节点之后,同时更新 start 节点和 curr 节点的位置。

最后,返回虚拟头节点的下一个节点,即为反转后的链表。

这段代码的时间复杂度为 O(n),其中 n 是链表的长度。

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

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

相关文章

天梯赛 L2-052 吉利矩阵

//r[n]:当前第几列的值。 //l[n]:当前第几行的值。 暴力减止 #include<bits/stdc.h> using namespace std; #define int long long const int n1e3; int a,b,c,l[n],r[n],an; void dfs(int x,int y) {if(xb1){an;return ;}for(int i0;i<a;i){l[x]i;r[y]i;if(l[x]&l…

JavaScript之模块化规范详解

文章的更新路线&#xff1a;JavaScript基础知识-Vue2基础知识-Vue3基础知识-TypeScript基础知识-网络基础知识-浏览器基础知识-项目优化知识-项目实战经验-前端温习题&#xff08;HTML基础知识和CSS基础知识已经更新完毕&#xff09; 正文 CommonJS、UMD、CMD和ES6是不同的模块…

详解Java中的五种IO模型

文章目录 前言1、内核空间和用户空间2、用户态和内核态3、上下文切换4、虚拟内存5、DMA技术6、传统 IO 的执行流程 一、阻塞IO模型二、非阻塞IO模型三、IO多路复用模型1、IO多路复用之select2、IO多路复用之epoll3、总结select、poll、epoll的区别 四、IO模型之信号驱动模型五、…

网络安全产品---态势感知EDR

态势感知 what SA&#xff0c;Situational Awareness 是对一定时间和空间内的环境元素进行感知&#xff0c;并对这些元素的含义进行理解&#xff0c;最终预测这些元素在未来的发展状态。 why 安全防护思想已经从过去的被动防御向主动防护和智能防护转变。如果不做到主动防御…

Hadoop1X,Hadoop2X和hadoop3X有很大的区别么?

Hadoop的演进从Hadoop 1到Hadoop 3主要是为了提供更高的效率、更好的资源管理、更高的可靠性以及对更多数据处理方式的支持。下面是Hadoop 1, Hadoop 2, 和 Hadoop 3之间的主要区别和演进的原因&#xff1a; Hadoop 1 特点&#xff1a; 主要包括两大核心组件&#xff1a;HDFS&a…

跨平台SIP 客户端-linphone下载、使用、开启视频H264

linphone 介绍 Linphone 是一种开源的语音和视频通信应用程序&#xff0c;它提供了基于互联网协议&#xff08;IP&#xff09;的实时通信功能。用于语音/视频通话、即时消息和电话会议的开源 SIP 电话。它适用于移动和桌面环境&#xff08;iOS、Android、GNU/Linux、macOS、Win…

【Linux】在centos快速搭建K8S1.18集群

使用 kubeadm 创建集群帮助文档 如果您需要以下几点&#xff0c;该工具是很好的选择&#xff1a;kubeadm 一种简单的方法&#xff0c;让你尝试 Kubernetes&#xff0c;可能是第一次。现有用户自动设置群集并测试其应用程序的一种方式。其他生态系统和/或安装程序工具中的构建…

SpringBoot集成Sleuth

引入Maven依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency> 配置yml文件 bootstrap.yml文件增加如下配置 注&#xff1a;这个配置不是必须要&#…

经典机器学习算法——决策树

优质博文&#xff1a;IT-BLOG-CN 树模型是机器学习中最常用的一类模型&#xff0c;包括随机森林、AdaBoost、GBDT&#xff08;XGBoost和Lightgbm&#xff09;等&#xff0c;基本原理都是通过集成弱学习器的即式来进一步提升准确度。这里的弱学习器包括线性模型和决策树模型&…

大sql mysql执行

先把sql 拆分 太大的执行失败 使用 SQLDumpSplitter3 拆分sql 执行拆分的sql 拆分的sql 打开发现很多 ; 开头的空行 替换掉 正则 ^; 修改数据库 my.cnf my,ini 执行可能会提示 [ERR] 2006 - Server has gone away 错误 在 [mysqld] 添加以下几行 wait_timeout2880000 inter…

上位机工作感想-从C#到Qt的转变-1

0.前言 接触Qt开发也有一年多的时间了&#xff0c;还记得去年初从杭州回合肥时&#xff0c;刚来公司面临的几个问题&#xff1a; 1.C#转上位机的迷茫2.新公司管理模式的差异3.试用期的各种紧急任务。 当时也是加班加点学习C和Qt的基础知识&#xff0c;做了两个考核项目后&am…

解决Mac使用Vscode无法调用外部终端

前言 今天遇到一个很奇怪的问题&#xff0c;之前好好的用Vscode还能调用外部终端&#xff0c;怎么今天不行了&#xff1f;问题出在哪里呢&#xff1f;请听我娓娓道来。 检查配置文件 我查看了一下配置文件&#xff0c;发现配置文件都是调用外部控制台&#xff0c;没毛病啊。 …

【AI开发:音频】二、GPT-SoVITS使用方法和过程中出现的问题(GPU版)

1.FileNotFoundError: [Errno 2] No such file or directory: logs/guanshenxxx/2-name2text-0.txt 这个问题中包含了两个&#xff1a; 第一个&#xff1a;No module named pyopenjtalk 我的电脑出现的就是这个 解决&#xff1a;pip install pyopenjtalk 第二个&#xff1a…

快速排序题目SelectK问题(力扣75.颜色分类、力扣215.数组中的第K个最大元素、面试题17.14最小K个数)

力扣75.颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sor…

从零开始的vscode配置及安装rust教程

配置vscode的rust环境 下载安装vscodemac 环境1. 下载安装rust2. 配置 mac vscode环境3. 创建一个测试项目 windows 环境1. 安装c运行环境2. 安装配置rustup3. 配置windows vscode环境4. 创建一个测试项目 下载安装vscode 1.官网应用程序下载 vscode&#xff1a;https://code.v…

注意力机制中多层的作用

1.多层的作用 在注意力机制中&#xff0c;多层的作用通常指的是将注意力机制堆叠在多个层上&#xff0c;这在深度学习模型中被称为“深度”或“多层”注意力网络。这种多层结构的作用和实现过程如下&#xff1a; 1. **逐层抽象**&#xff1a;每一层都可以捕捉到输入数据的不同…

在ubuntu20.04下迁移anaconda的目录,试验不行后,换成软连接

一、原因 随着不断的搭建不同的算法环境&#xff0c;原本在固态硬盘上安装的anaconda上占用空间越来越多。导致可用的固态硬盘空间越来越少&#xff0c;又因安装的环境太多&#xff0c;重新搭建比较费时费力。有没有直接将当前已经搭建好环境的anaconda 迁移到另外的目录呢&…

SAP 销售业务中免费货物的会计核算

此博文主要介绍SAP销售业务中免费货物解决方案中&#xff0c;免费货物的会计核算。如果需要进一步了解SAP SD 销售与分销业务中&#xff0c;免费货物的标准解决方案概览&#xff0c;可先了解本博客博文&#xff1a;SAP销售与分销中的免费货物解决方案相关文章&#xff1a; htt…

Python 全栈安全(一)

原文&#xff1a;annas-archive.org/md5/712ab41a4ed6036d0e8214d788514d6b 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 序言 多年前&#xff0c;我在亚马逊搜索了一本基于 Python 的应用程序安全书。我以为会有多本书可供选择。已经有了很多其他主题的 Pyt…

【设计模式】单例模式|最常用的设计模式

写在前面 单例模式是最常用的设计模式之一&#xff0c;虽然简单&#xff0c;但是还是有一些小坑点需要注意。本文介绍单例模式并使用go语言实现一遍单例模式。 单例模式介绍 简介 单例模式保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 使用场景&#…