数据结构——链表OJ题目讲解(2)
作者:几冬雪来
时间:2023年3月10日
内容:数据结构链表OJ题目讲解
来源:牛客网和力扣
目录
前言:
刷题:
1.反转链表:
1.改变指向的解法:
2.取头结点插入到新链表:
2.合并两个有序链表:
1.正常尾插:
2.带哨兵位尾插:
3.链表分割:
4.链表的回文结构:
5.相交链表:
结尾:
前言:
在上一篇博客中我们讲解了一些有关链表的OJ题目,但是在刷题的过程中我们的题目并没有覆盖到方方面面,有一些链表拓展出来的新的写法我们没有讲解到。因此今天我们又选取了几道题目来进行我们的讲解。

刷题:
刷题是我们在学习数据结构又或者是学习语言等必不可少的过程,我们通过刷题可以了解到一些不同的解题思路和方法。也可以发现不少我们写题的时候可能会犯错的地方,因此刷题是一种可以提升我们解题能力的一种手段。
1.反转链表:

反转链表作为数据结构链表的一道经典题目,如今还在许多公司等作为面试的题目使用。
1.改变指向的解法:
这道题用指针的解法来解,因为要调换前后两个结点的指向,因此单指针绝对是不行的,起码都要是双指针。但是这道题仅仅是双指针还是不够的,假设我们让第二个结点指向第一个结点,这样两者的指向就交换了,但是这个时候第二个结点不再指向第三个结点,我们就会找不到第三个结点了。因此,在这里我们还需要创建一个指针用来保存我们的第三个结点。

那我们这个程序什么时候结束呢?是n3为空的时候吗?并不是,这个程序我们结束的标志是n2为空的时候才结束。

这里一开始先创建三个指针,指向就是上面画的那种图,n1指向空,n2指向头结点,n3指向n2的下一个结点。然后开始循环,因为n2为空我们的编程结束,所以循环条件也就为n2。接下来就是翻转,翻转前是n1->next = n2那么将其翻转后就是n2的下一个结点指向n1,也就是n2->next = n1。接下来n1,n2,n3全部向后移动一个位置,然后再次循环,最后返回n1指针。

运行代码后,代码出现了错误,根据报错提醒我们的n3 = n3->next会出现空指针的问题,因为代码是n2为空的时候结束,当n2指向尾结点的时候,n3就已经为空了。这个时候n3 = n3->next会出错就很正常。因此,在这里我们要给n3的执行加上一个条件。

如果n3已经为空的话,我们的n3 = n3->next这个代码就不用执行了。这样就能保证n3不会出现空指针的问题。再次运行代码。

当我们的链表为空的时候,这个代码又出错了,如果链表为空的话,我们这里就没有头结点。下面的赋值多多少少都会出错,因此在一开始我们就要对它是否为空进行判断。

这样我们的代码就通过了。那么下来我们就将代码放上来。

当然这个问题我们两种方法来解决。
2.取头结点插入到新链表:
对于这道题我们的另一种解决方法就是取头结点,然后将其插入到新链表。这种方法我们在学习数据结构链表的时候也经常用到,这里我们就画图说明一下即可。

在这里我们需要创建两个指针,一个指针用于插入另一个指针用于保存数组,直到原链表的所有结点都插入完毕即可。

开始讲我们给原链表的头文件定义一个指针cur,而后定义一个空指针作为新链表。接下来就是循环,因为要结点插入,有多少个结点就插入多少个,这里的循环条件就为cur。然后进入循环,先创建一个指针保存cur->next,而后开始头插,将cur->next指向空作为我们的新链表的尾结点。接下来newhead要进行更新,cur也指向下一个结点。
运行代码起来看看。
用例都通过,这就证明我们的代码和思路没有问题,同时相比较上面那种方法,取头结点插入新链表的代码量明显少了。
2.合并两个有序链表:

下来就是一道新的题目了,题目要求我们合并合并两个有序链表,形成一个新的升序的链表,然后将其返回。而我们要做的就是将两个链表的结点依次比较,取小的值进行尾插。
1.正常尾插:

这个就是我们的操作原理,就是两个链表之间的结点进行相比较,最终形成一个新的链表。下来就来看看我们的代码是怎么样的。

在这个代码一开始创建两个指针指向两个链表的头结点,同时再创建两个指针作为新的链表的头结点。接下来进入循环,无论哪一个指针先走我们就结束循环。然后就是判断,如果cur1的值小于cur2的话,我们就进行if语句。
首先对新链表是否为空进行判断,如果head为空的话,这里进行的操作就不是插入而是赋值操作。如果不为空,则将cur1的值给tail->next,然后tail结点进行更新,向后走一步,同时cur1也向后走一步来到它的下一个结点。如果cur2大于cur1,则也是如果操作,只不过将里面的cur1更换为cur2罢了。
注:因为我们cur1并没有被修改,因此不需要一个新指针保存下一个结点。
最后如果哪个链表先走完,那就将另一个链表直接并在新链表的后面,因为我们这里并没有修改原链表的值,因此只要将一个结点链接到我们新链表的后面就可以了,后面的结点本身就是链接到一起的。

但是我们的这个代码是存在问题的,也就是空指针的问题,看用例的话则是第一个指针为空的情况。如果链表为空,那么我们的赋值操作就不会进行。

因此我们要在一开始对其进行判断,是否为空的情况。

如果list1为空我们就返回list2,反之返回list1。

修改过后我们的代码也是成功的通过了所有的用例。那么还是老规矩,运行成功之后我们要将代码给放上来。

2.带哨兵位尾插:
既然上面那种方法已经了解了,那么接下来我们要介绍一种新方法——带哨兵位的链表操作。
在我们上边的代码中,书写的时候要注意很多空指针的问题。例如两个链表一开始是否为空,第一次插入新链表的时候要判断是否为空来决定是赋值操作还是插入操作。
同时哨兵位也不存放我们的有效值。
但是这里采用哨兵位的解法就没有这些问题了,那么哨兵位是怎么样操作的呢?

类似这种方法,一开始我们就创建一块空间,使我们新创建的两个指针指向这块空间,因为两个指针都存在有空间所以它们不可能为0。那么接下来我们应该怎么对我们的代码进行修改呢?

这里我们就创建两个指针,并让指针指向一块空间。然后将tail->next置空,为链表的插入做准备。这里因为tail有指向一块空间不为空,因此判断结束准备插入的时候不用再对tail是否为空进行判断。下面的进程是一样的,最后因为链表是没有哨兵位的,所以结束时要将创建的那块空间进行一个释放。最后返回我们新链表的头结点的位置。

最后结果也可以看出来我们的思路并没有错误。
3.链表分割:

这道题是我们从牛客网中找到的题目,链表分割在牛客网给出的难度是困难,但是在力扣里面难度可能会变为中等,不过即使这样我们依旧要解决这道题。
这道题我们依旧要创建哨兵位来求解,这样子会更加方便。然后进行尾插操作,小于x的尾插到一个链表中,大于等于x的分装到另一个链表上去,最后将两个链表相链接。
假设我们输入的为1 5 2 3 7 4

多说无益,接下来我们就通过写代码来证明。

这里我们先创建四个指针,两个固定指针,两个移动指针。然后创建两个哨兵位的头结点给两个固定的指针。接下来把两个移动指针的下一个位置置空用于链表链接使用。紧接着创建一个指针,指向原链表的头结点,下来开始循环如果cur->next小于x,那么我们就将其插入到小于x的链表中,反之插入到大于等于的链表中,又因为我们插入了新结点,所以我们的lTal或者gTail需要更新往下走一步,同时cur也向下走一步。最后将两个链表链接在一起,并将哨兵位删除释放。

多少运行起来我们的代码就出现了问题,这个代码的问题就是发生了内存超限。为什么会内存超限?明明没有用到多少内存,这里就出现了链表中一个经典的问题——环问题。

这里可以看见我们的链表链接后,最后一个结点指向的并不是空而是3,这是因为我们这里插入方法采取的是尾插,不改变本次插入的next指向,只是改变上一个结点的next指向。
因此在最后将其链接后最后一个结点的next置空即可。

这里不知道为什么没有通过,我也不知道哪里错了。但是思路应该是没问题的。
4.链表的回文结构:

那么什么是链表的回文呢?简单来说就是链表的对称。

那么我们该怎么解决这道题呢?这道题也有一种很简单的方法。

1.找到中间结点
2.从中间结点开始,对后半段进行逆置
3.前半段和后半段进行比较
这就是我们这道题的解决思路。这里有人就要问了:偶数逆置看得懂,但是奇数的链表逆置后变为了1 2 1 2 3是不一样的,怎么可能成立,但是其实这个地方即使是逆置了,前一个2的next的指向依旧指向3,并没有被修改。
那么我们该怎么书写我们的代码呢?这里我们可以偷个懒,从我们往期博客中将反转链表和链表的中间结点两道题目的代码复制过来使用。

一开始我们就使用反转链表和链表的中间结点题目的代码,分别求出中间结点,并且将中间结点后面的所有结点进行逆置操作,并将中间结点的位置设计为一个新的头结点。然后进行判断,在head&&rhead中,如果二者中有一个不相等我们就返回false,相反如果相同的话我们的两个头结点就往后走一步。如果循环结束了我们的head依旧等于rhead,这个时候返回true就可以了。

最后通过测试也是可以看出我们的代码并没有什么问题。那么我们这道题目也就成功解决了,接下来就是另一道题目了。
5.相交链表:

在数学中我们经常遇到函数相交的情况。

两条函数相交与一个点,那么我们可以说这两条函数相交,这个相交的地方也被我们称为交点。那么我们的相交链表也是这样的吗?如果这样想那就大错特错了。

这种理解是错误的,因为当我们链表相交之后,它们会交于一个共同的结点,而这个结点只有一个next,它只会指向下一个结点,并不会指向两个不同的结点。
那我们要怎么判定两个链表相交了呢?

这里我们通过判定尾结点的地址来看看两个链表有没有相交。如果两个链表的尾结点的地址相同,那么它们就是相交的,如果不同那就不相交。
注:判断的是尾结点的地址而不是尾结点的值。
但是如果这个样子,那么我们要怎么样找到交点?有人说找到a2和b3的next即可,但是这里我们又怎么确定a2和b3在哪里? 那么这里最简单粗暴的就是直接暴力比较。拿a1与b1~c3相比较,如果都不相等,那么就拿a2和b1~c3再次相比。但是如果是这种方法的话,我们的时间复杂度就为O(N^2),它就是一个很大的值了。
如果题目有给我们一个限制,要求我们的时间复杂度为O(N)空间复杂度为1,那这道题我们又应该怎么办解。

那说到这里,我们就动手写代码吧。

这里一开始我们就创建两个指针来存放两个链表的头结点,并定义两个变量赋值为1。然后就是循环判断,先找两个链表的尾结点并且lenA和lenB都要移动,为了后面比较长短做准备,如果尾结点不同我们就直接返回空。如果相等的话,我们就创建一个变量来存放差距步的多少,因为这里不知道两个链表哪个比较长,因此在这之后还要再创建两个指针指向我们的头结点,一开始我们就直接先假设A链表比B链表长,然后判断,要是链表A没有链表B长,我们就将两个值调换。
接下来我们的长链表就要先走差距步,gap就是差距步的个数。当差距步走完后我们就开始判断,如果longList不等于shortList,说明二者并没有指向同一个地址,这里的两个结点就要同时向后走一步,如果找到了就直接返回指针的地址。

最后我们这个代码也是通过了用例。
结尾:
本次我们链表OJ的题目也就到这里结束了,在这篇博客中又学习到了不少新知识——哨兵位的头结点,但是还是有一些链表的题目我们并没有讲到,例如后面的链表环形结构等。不过这些题目也并不简单,后面要熟悉链表的得认真反复的去学习。最后希望这一篇链表OJ题目的讲解可以为各位带来帮助,让大家更了解链表知识。
相关文章:
数据结构——链表OJ题目讲解(2)
作者:几冬雪来 时间:2023年3月10日 内容:数据结构链表OJ题目讲解 来源:牛客网和力扣 目录 前言: 刷题: 1.反转链表: 1.改变指向的解法: 2.取头结点插入到新链表: …...
GitHub上线重量级分布式事务笔记,再也不怕面试官问分布式了
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式…...
C++语法规则1(C++面向对象 )
C面向对象 面向对象的三大特征是继承,多态和封装,C重面向对象重要的就是这些,我们下面通过一些简单的实例加以理解,从这小节开始,我们将开启新的编程旅途。与 C 语言编程的思想完全不同了,这就是 C!理解概…...
Web漏洞-CSRF漏洞
CSRF漏洞介绍:CSRF(Cross-Site Request Forgery),中文名称:跨站请求伪造,是一种劫持用户在当前已登录的Web应用程序上执行非本意操作一种攻击.原理:攻击者利用目标用户的身份,执行某…...
Python3-面向对象
Python3 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象…...
拐点!新能源车交付均价首次「低于」燃油车,智能电动成新爆点
2023年开局,随着特斯拉打响新能源汽车市场的「价格战」首炮,除部分燃油车品牌(仍依赖自身多年的用户和品牌积累的溢价能力)没有跟进之外,几乎所有的新能源车型都在进行车型价格的下调。 而数据也在反映市场的拐点即将来…...
JavaScript String 字符串对象实例合集
文章目录JavaScript String 字符串对象实例合集返回字符串的长度为字符串添加样式返回字符串中指定文本首次出现的位置 - indexOf()方法查找字符串中特定的字符,若找到,则返回该字符 - match() 方法替换字符串中的字符 - replace()JavaScript String 字符…...
实习生培养计划
部门最近入职了实习生,为了更好的帮助实习生完成由学生向职业人的转变,并尽快融入企业稳步成长,现提出实习生培养计划,具体方案如下: 1、方案目的 1、使实习生快速转换角色与心态,适应从校园到企业的坏境…...
【服务器管理】Wordpress服务器内存占用太高(优化方案详解)
简述 在刚刚配置完服务器之后,想着试一试wordpress这个功能,结果打开服务器后台,发现本来就不多的内存被占用了一大半。 我真的服了,我还啥都没干呢,就这么多的内存占用,那之后我开始弄了还得了。因此有必…...
【ECCV 2022】76小时动捕,最大规模数字人多模态数据集开源
随着元宇宙的火爆以及数字人建模技术的商业化,AI 数字人驱动算法,作为数字人动画技术链的下一关键环节,获得了学界和工业界越来越广泛的兴趣和关注。其中谈话动作生成 (由声音等控制信号生成肢体和手部动作)由于可以降…...
联合解决方案 | 亚信科技AntDB数据库携手浪潮K1 Power赋能关键行业数字化转型,助力新基建
自2022年印发《“十四五”数字经济发展规划》以来,我国数字化发展进入快车道。数据库作为数据存储与计算的基础软件,对筑牢数字经济底座至关重要。服务器是承载数据的重要载体,在数据库性能可以通过扩容而无上限提升的情况下,数据…...
Android 单元测试问题总结(Robolectric+JUnit)
代码单元测试问题总结: 1、测试类中引用第三方jar包类报错 问题原因: 测试的库中没有包含第三方jar包。 解决办法: 在app下gradle中加入第三方jar包配置: testImplementation files(‘libs/third.jar’) 2、自定义Shadow类不生…...
专项攻克——二叉树
文章目录一、二叉树定义、分类二、二叉树的存储结构三、创建二叉树四、遍历方式一、二叉树定义、分类 二叉树:是N个结点的有序集合,该集合或者为空集,或者由一个根节点跟两棵互不相交的、分别称为根节点的左子树或者右子树的二叉树组成。每个…...
PACS系统源码 PACS源码 三维重建PACS源码
一、系统概述: 基于VC MSSQL开发的一套三甲医院医学影像PACS系统源码,集成3D影像后处理功能,包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分析等功能。系统功能强大,代码…...
利用Mysql存储过程造百万级数据
1.准备工作(1)由于是使用存储过程,mysql从5.0版开始支持存储过程,那么需要mysql的版本在5.0或者以上。如何查看mysql的版本,使用下面sql语句查看:(2)创建两张表,表结构一…...
Vue2组件之间的传值通信
父子组件Vue中常见的是父与子组件间的通信,所要用到的关键字段是props和$emit。props接受父组件传给子组件信息的字段,它的类型:Array<string> | Object;详细解释可以参考https://cn.vuejs.org/v2/api/#props$emit由子组件触发事件向上…...
Spring Boot官方例子《Developing Your First Spring Boot Application》无法运行
官方的第一个例子就卡住了: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#getting-started.first-application 按照要求,一步一步走: 查看Java版本和MVN版本: $ java -version openjdk version &quo…...
数据结构(3)— 线性表之顺序存储详解介绍(含代码)
(1)博客代码在数据结构代码---GitHub仓库;线性表介绍线性表的基础概念(1)甲骨文表示:线性表是零个或多个数据元素的有限序列。(2)线性表,顾名思义,就是说这个…...
ChatGPT正当时,让我们一起深耕智能内容生成和智能内容增强领域
ChatGPT以其强大的信息整合和对话能力惊艳了全球,在自然语言处理上面表现出了惊人的能力。很多人都预测 2023 年将是 AI 生成之年,也许我们将迎来继农业革命、工业革命以来的第三种通用技术的普及。 信必优长期专注于人工智能领域,拥有产品研…...
天梯赛训练L1-019 (谁先倒)
目录 1、L1-019 谁先倒 2、如果帮到大家,请大家一键三连!!! 3、读书吧,在落幕无光时找到方向!!! 1、L1-019 谁先倒 分数 15 题目通道 划拳是古老中国酒文化的一个有趣的组成部分…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
