链表算法题(OJ刷题超详细讲解)

1.返回倒数第K个节点,

OJ链接:返回倒数第K个节点

本题有很多种解法,例如创建数组,或者将原链表反转等等,这里们使用快慢指针,只需要遍历一遍链表,并且空间复杂度为O(1),时间复杂度为O(N)

我们先来画图分析一下 

我们让快指针fast先走k步,然后再一起走,最后返回slow的值。

代码如下:

typedef struct ListNode ListNode;int kthToLast(struct ListNode* head, int k){assert(head);ListNode*slow = head;ListNode*fast = head;while(k--){fast = fast->next;}while(fast){fast = fast->next;slow = slow->next;}return slow->val;}

2.链表的回文结构

OJ链接:链表的回文结构

本题要求时间复杂度为O(n),额外空间复杂度为O(1),该怎么写呢?

分析:

我们要想证明它是个回文结构,首先我们要先知道回文结构是什么,从前往后和从后往前是完全相同的,以中间节点为中心这个链表是对称的,举个例子比如12321和1221这两个都是回文,如果是123123这个还是回文吗?这种就不是回文了,如果需要证明这个链表是回文,那么我们是不是就可以先找到中间节点,然后再反转一下中间节点及之后的节点,如上图我们找到中间节点2之后把中间节点及之后的节点都反转了,然后让中间节点与首节点进行判断值是否相等,万一是奇数个节点呢,奇数个也没事我们左边的2并没有改变它的next所以2指向的还是3,3和3自己比,最后3指向的就是NULL了。

代码如下:

//找中间节点
struct ListNode* middleNode(ListNode* head)
{struct ListNode* slow = head,*fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}return slow;
}
//反转
struct ListNode *reverseList(ListNode *head)
{struct ListNode *cur = head;struct ListNode* newhead = NULL;while(cur){struct ListNode*next = cur->next;cur->next = newhead;newhead = cur;cur = next;}return newhead;
}bool chkPalindrome(ListNode* A) {// write code herestruct ListNode*mid = middleNode(A);struct ListNode*rmid = reverseList(mid);while(rmid && A){if(A->val != rmid->val)return false;A = A->next;rmid = rmid->next;}return true;
}
};

3.相交链表

OJ链接:相交链表

判断链表是否相交,如果说我们一个一个比较的话,A链表的节点依次跟B链表所有节点比较,A某个节点跟B某个节点相等,这个节点就是交点,这样太麻烦了,而且时间复杂度为O(N^2),那么我们可以先遍历链表,判断两个链表的尾节点是否相等,如果相等那么就一定相交,在我们遍历链表的时候,我们可以顺便计算出链表的相对值,然后让长的链表先走差值的距离,在同时走,只要两个节点相等就是相交

画图演示:

代码如下:

 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {ListNode*curA = headA,*curB = headB;int lenA = 1,lenB = 1;while(curA->next){curA = curA->next;++lenA;}while(curB->next){curB = curB->next;++lenB;}//尾节点不相等就是不相交if(curA->next != curB->next){return NULL;}//长的先走差距步,在同时走,第一个相等就是相交//假设法int gap = abs(lenA-lenB);//求绝对值ListNode* longList = headA,*shortList = headB;if(lenB > lenA){longList = headB;shortList = headA;}while(gap--){longList = longList->next;}while(longList != shortList){longList = longList->next;shortList = shortList->next;}return shortList;
}

如上代码中我们用到了假设法,如果我们假设错了,就修正一下,A长还是B我们不关注,我们只要知道longList是长的shortList是短的就行,假设法可以使得我们逻辑变得更加简单。

注:我们一定要用地址进行判断,如果用值判断可能会有重复的值


4.随机链表的复制

OJ链接:随机链表的复制

深拷贝就是拷贝一个值和指针指向跟当前链表一模一样的链表

这道链表题每个节点里多了个指针指向随机节点,也有可能指向空,然后深拷贝一份,如果我们直接遍历然后拷贝呢?硬拷贝是可以的,但是有个问题,随机指针(random)指向的值如何在新链表中实现呢,如果我们去链表中查找,那么万一有重复的值怎么办呢,这就会导致没拷贝成功,如果真要用这种暴力查找,就要算相对位置,而且它的时间复杂度为O(N^2)。

我们可以先拷贝链表,然后把拷贝的链表插入到原节点的后面,每个拷贝节点和原节点建立了一个关联关系

然后我们在控制random,如下图所示,我们7的random指向的为空,那么我们拷贝节点7的random也要指向空,我们拷贝节点7就在原节点的后面,所以处理起来很方便,那么13的random指向的是7我们如何拷贝呢,拷贝节点的random指向的就是cur->random->next,最后再将拷贝节点拿下来尾插,成为一个新的链表,虽然我们破坏了原链表的结构,我们可以选择恢复原链表也可以不恢复,题目没有要求。

代码如下:

typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {Node* cur = head;// 拷贝节点插入在原节点后面while(cur){Node* copy = (Node*)malloc(sizeof(Node));copy->val = cur->val;copy->next = cur->next;cur->next = copy;cur = copy->next;}//控制randomcur = head;while(cur){Node*copy = cur->next;if(cur->random == NULL){copy->random = NULL;}else{copy->random = cur->random->next;}cur = copy->next;}//拷贝节点取下来尾插成为新链表,然后恢复原链表(不恢复也可以)Node *copyhead = NULL,*copytail = NULL;cur = head;while(cur){Node*copy = cur->next;Node*next = copy->next;if(copytail == NULL){copyhead = copytail = copy;}else{copytail->next = copy;copytail = copytail->next;}//恢复原链表cur->next = next;cur = next;}return copyhead;
}

5.环形链表

OJ链接:环形链表

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

画图演示:

 代码如下:

bool hasCycle(struct ListNode *head) {struct ListNode*slow = head,*fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;if(slow == fast){return true;}}return false;}

在面试中面试官可能会问的两个问题,如下

1.为什么一定会相遇,没有可能会错过,永远追不上?请证明 

证明:假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可 能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动 一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前, 快指针肯定是可以追上慢指针的,即相遇

2.slow一次走一步,fast走3步4步5步n步能追上吗?请证明

证明如下:

这里我们假设fast一次走3步,当slow进环时,fast开始追击slow,过程中距离变化如下:

偶数                        奇数

N                              N

N-2                           N-2

N-4                           N-4

....                             ....

4                                3

2                                1

1                                -1

0                

每追击一次,距离缩小2

0就是追上了

追成-1就是刚好错过,开始新一轮的追击了

距离变成C-1,(假设C是环的长度)

总结一下:

1、N是偶数,第一轮就追上了

2、N是奇数,第一轮追击会错过,距离变成C-1

        a、如果C-1是偶数,下一轮就追上了

        b、如果C-1是奇数,那么就永远追不上

我们在来分析一下会不会出现永远追不上的可能,上面的b条件成不成立呢?

永远追不上是有条件的,同时存在N是奇数且C是偶数,那么就永远追不上

那么有没有可能这两个条件不存在?证明一下

证明如下:

假设slow进环时,fast跟slow距离是N

slow走的距离是L

fast走的距离:L+x*C+C-N

slow进环时,假设fast已经在环里转了x圈

fast走的距离是slow的3倍

3*L= L+x*C+C-N

化简一下变成:2*L = (x+1)*C-N

偶数 = (x+1)*偶数-奇数

N是奇数时,C也是奇数

N是偶数时,C也是偶数

只有奇数-奇数才能变成偶数

(x+1)*偶数一定是偶数

所以N是奇数且C的偶数不能同时存在,永远追不上的条件不成立

结论:

N是偶数,第一轮就追上了

N是奇数,第一轮追不上,C-1是偶数第二轮就追上


6.环形链表II

OJ链接:环形链表II

我们依旧可以使用快慢指针,slow走一步fast走两步,当它们在环里相遇时,让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行, 两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

在我们面试的时候面试官可能会问这样的问题,为什么它们最终肯定会在入口点的位置相遇呢?请证明一下?

证明如下:

假设入环前的这段距离是L

假设相遇点到入环点的距离是N

长的环度是C

假设x是fast在环里转了x圈(x>=1)

相遇时:

slow走的路程:L+N

fast走的路程:L+x*C+N

fast走的路程是slow2倍

2*(L+N)=L+x*C+N

L+N = x*C

L=x*C-N

最终化简成这样:L=(x-1)*C+C-N

假如x=1,那么L=C-N,正好是相遇点到进环点的距离与入环之前的距离相等,让一个节点从头开始遍历,一个从相遇点开始,最终在入环的第一个节点相遇,证明了我们肯定会在入口点的位置相遇,如果x不唯1呢,那也会相遇,无非就是在环里多走了几圈,最后还是会在入环的第一个节点相遇

代码如下:

struct ListNode *detectCycle(struct ListNode *head) {struct ListNode*slow=head,*fase = head;while(fase && fase->next){slow = slow->next;fase = fase->next->next;//相遇if(slow == fase){struct ListNode*meet = slow;while(meet != head){meet = meet->next;head = head->next;}return meet;}}return NULL;
}

总结

经过这次对链表算法题的深入解析,链表作为基础数据结构,其相关算法是编程能力的试金石。通过在线刷题平台,我们可以不断挑战自我,深化对链表的理解,提高算法设计能力。

刷题能够让我们更加熟悉各种算法和数据结构,掌握它们的基本操作和应用场景。通过大量的实践,我们能够加深对理论知识的理解,形成自己的编程风格和思维方式。

刷题不是目的,应用才是关键。让我们通过刷题,不断提升编程水平,将所学知识应用于实际项目中,成为更优秀的开发者。

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

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

相关文章

选择困难!?伦敦金etf谁胜一筹?

虽然伦敦金和黄金ETF都是黄金的投资方式,但它们有着许多不一样的地方。伦敦金市场是全球最大的黄金市场之一,每天的交易量巨大,没有任何财团或机构能够人为操控它,而且整个市场24小时公开地运行,投资者可以灵活把握市场…

HCIP-RS实验-OSPF单区域配置

目录 简介:建立新拓扑配置IP验证IP连通性配置单区域的OSPF查看网络连通性查看路由器运行的基本OSPF信息查看路由器的OSPF邻居关系建立情况查看路由器的OSPF数据库信息。 观察路由器在以太网上邻接关系的建立过程 简介: OSPF(Open Shortest P…

在C#中使用 NLog 库进行日志记录

NLog 是 .NET 的日志记录框架。具有丰富的日志路由和管理能力,极大地帮助您生成和管理日志。NLog 是一个库,可以轻松地同时记录和管理多个不同区域(例如控制台、数据库或文本文件)中的数据。我们可以按如下方式列出 NLog 的功能&a…

实战攻防:蜜罐无关,溯源有术

前言 突然接到通知,甲方在HVV防守前突然收到内网IP地址出现Socks代理通信,审计流量发现确实属于socks流量,不属于告警流量,告警地址为个人终端,直接准入工具阻断等待排查。 态感告警 流量分析属于正常的socks流量。 …

职场思考-职场第三年规划重点(14)

(职场第三年规划重点(上)) 确定方向,拉进关系,提高思维 好的职业生涯规划必须具备以下三个特点: 合理性(以当事人的天赋、性格和现有知识与技能为基础,是适合并为当事人所接受的)、可…

word-简历排版

1、确认字体(微软雅黑)、字号(五号/小五) 2、设置段间距和行间距、页边距 3、突出各模块标题,增加分格线 4、使用制表位进行对齐:视图-标尺,制表符(tab)和制表位共同使…

西贝柳斯终极版2023:Mac上的简易音乐记谱神器,谱写未来

Avid Sibelius Ultimate 2023 for Mac是一款专为Mac用户设计的音乐记谱软件,它以其强大的功能和直观的操作界面,为音乐创作者们提供了一个高效、便捷的创作平台。 一、音乐创作的得力助手 Sibelius Ultimate 2023不仅适用于有抱负的作曲家和词曲作者&a…

计算机网络学习记录 网络层 Day4(上)

计算机网络学习记录 网络层 Day4 (上) 你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner gitee https://gitee.com/Qiuner 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f60…

122. 买卖股票的最佳时机 II(中等)

122. 买卖股票的最佳时机 II 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转:122. 买卖股票的最佳时机 II 2.详细题解 实现最大的利润,即只要有盈利就收入囊中,由于交易没有具体限制次数,因此可以依…

再论Web应用在医学研究中构建数据收集问卷(stremlit_survey包体验)

再论Web应用在医学研究中构建数据收集问卷(Streamlit_survey包体验) 概述 医学队列研究是临床研究的重要形式,这种研究通过收集临床诊疗过程中产生的数据而阐述疾病相关的因素。在临床数据收集过程中,Web APP体现出了一定的优势…

计算机毕业设计 | SpringBoot图书管理系统(附源码)

1, 概述 1.1 课题背景 开发一个学生成绩管理系统,采用计算机对学生成绩进行处理,进一步提高了办学效益和现代化水平。为广大教师和学生提高工作效率,实现学生成绩信息管理工作流程的系统化、规范化和自动化。现在我国中学的学生…

YOLOv8_obb训练流程-原理解析[旋转目标检测理论篇]

在旋转目标检测网络中,换了个顺序,先把训练流程捋一遍,然后再取捋一下测试的流程。由下图的YOLOv8l_obb网络结构图可以看到相对于目标检测网络,旋转目标检测网络只是在Head层不相同,在每个尺度特征层中增加了Angle分支(浅蓝色),通过两个卷积组和一个Conv卷积得到得到通…

jupyter之plt 画图弹出窗口展示图片以及静态图片切换方法

1. jupyter出图的三种方式 在python的Jupyter Notebook中,使用matplotlib绘制动态图形时,可能出现只显示一张静态图像。 这是因为在notebook中使用plt绘图共有三种模式: %matplotlib inline:这是默认的模式,输出的图片…

C语言Prim算法和Prim-Alternat找最小生成树

文章目录 1、用prim算法求最小生成树C语言Prim算法实现 2、用Prim-Alternate算法求最小生成树3、C语言Prim-Alternate算法实现 1、用prim算法求最小生成树 绿色线会标记选过的边 从v1当作起始点开始,可选择: (v1,v2)权值为6 (v1,v3)权值为3 &…

经济学SSCI期刊,中科院1区,领域内顶刊,影响力高

一、期刊名称 World Development 二、期刊简介概况 期刊类型:SSCI 学科领域:经济学 影响因子:6.9 中科院分区:1区 三、期刊征稿范围 《世界发展》是一本多学科的发展研究月刊。它力求探讨如何改善生活水平和一般人类状况&am…

AIGC绘画基础——Midjourney关键词大全+万能公式

距发布MJ初级注册入门教程已有时日,很多粉丝表示很有用,但关键词有很多人不知如何组合使用,那今天再给大家更新一期,主要是教大家如何用关键词、把控关键词描述,除此之外在文末更新了一大堆关键词给大家使用~ 一、Midj…

合并两个有序链表和合并 K 个升序链表

21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2: 输入:l1 [], l2 […

NFTScan | 05.27~06.02 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期:2024.05.27~ 2024.06.02​ NFT Hot News 01/ Mint Blockchain 披露最新路线图,释放 NFT 生态重磅发展计划 5 月 28 日,Mint Blockchain 开发者团队 MintCore 更新…

Arduino 串口接收数据

1、上位机发送十六进制 AA 01 DE 下位机回复AC,上位机发送十六进制 AA 02 DE 下位机回复AB。如下图所所示。 2、Arduino 代码如下。 #define ReceiveLen 100 // 接收数据数组长度 byte ReceiveData[ReceiveLen]; // 接收数据数组void loop() {// 串口接收数…

jadx-gui-1.5 反编译工具使用教程 反混淆 Java android 查看签名

JADX:JADX是一个强大的反编译工具,它支持命令行和图形界面操作。除了基本的反编译功能外,JADX还提供了反混淆功能,有助于提高反编译后代码的可读性。 在Android开发和安全分析领域,反编译工具扮演着至关重要的角色。这…