链表OJ--超详细解析

链表OJ

文章目录

  • 链表OJ
    • 1. 反转链表
    • 2. 返回K值
    • 3. 链表的中间节点
    • 4. 回文链表
    • 5. 相交链表
    • 6. 带环链表
      • 6.1 为什么一定会相遇,有没有可能会错过,或者出现永远追不上的情况,请证明
      • 6.2 slow一次走一步,fast如果一次走3步,走4步,走5步还能追上吗,请证明
    • 7. 带环链表2
      • 7.1 为什么它们最终肯定会在入口点的位置相遇,请证明
    • 8. 复制链表
    • 结语

1. 反转链表

OJ链接:206. 反转链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:可以新创建一个链表,然后采用头插的方法,从 1 一个接一个的头插数据,这种是最容易想到的方法,但是要开辟一块空间,需要考虑空间复杂度的问题

思路二:使用 n1 n2 n3 三个指针,首先我们让 n1 指向空, n2 指向头节点, n3 指向头节点的下一个节点
在这里插入图片描述

然后我们再让 n1 指向 n2 , n2 指向 n3 ,就变成了这样

在这里插入图片描述

这样的话 n1 是不是指向反转链表之后的尾节点,那我们如果再让 n1 指向 n2 , n2 指向 n3 ,把整个链表都给遍历一遍之后,是不是就把整个链表给反转了,对吧,像这样

在这里插入图片描述

然后我们在考虑遍历截止的条件,哎,是不是 n2 为空了之后遍历就截止了,最后再返回 n1 就好了

整体的思路是不是都比较清楚了,接下来就是代码实现了,这个就得多练习了

struct ListNode* reverseList(struct ListNode* head) {if(head == NULL){return NULL;}struct ListNode* n1 = NULL,*n2 = head,*n3 = n2->next;while(n2){n2->next = n1;n1 = n2;n2 = n3;if(n3)n3 = n3->next;}return n1;
}

2. 返回K值

OJ链接:面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:我们可以创建一个数组,把链表中的数据存放在数组里,用数组来做,不过要再开辟一块空间,还要考虑空间复杂度的问题

思路二:反转链表遍历一下,再找第K个节点,这样也是可行的

思路三:使用快慢指针的方法,我们先让快指针走K步,然后再让快慢指针一起走,就能发现当快指针指向空的时候,我们的慢指针指向的就是倒数第K个节点,最后返回慢指针节点的值就行了

在这里插入图片描述

代码实现:

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

3. 链表的中间节点

OJ链接:876. 链表的中间结点 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路:主要的思路还是我们的快慢指针,这时就要分两种情况,一个是奇数的情况,另一个是偶数的情况

  • 当链表为奇数时:首先我们要初始化两个快慢指针,然后通常让慢指针走一步,快指针走两步,像这样

在这里插入图片描述

像这样,当快指针指向尾节点的时候,慢指针就刚好指向中间节点,最后返回慢指针节点的值

  • 当链表为奇数时:首先我们还要初始化两个快慢指针,然后还是让慢指针走一步,快指针走两步,像这样
    在这里插入图片描述

就像这样,和前者不同的是,当快指针指向空时,慢指针才指向中间节点,最后返回慢指针节点的值

思路都清楚了吧,接下来就是代码实现:

struct ListNode* middleNode(struct ListNode* head) {struct ListNode* fast = head,*slow = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}return slow;
}

4. 回文链表

OJ链接:234. 回文链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

我们要想证明它是个回文结构,首先我们要先知道回文结构是什么,从前往后和从后往前是完全相同的以中间节点为中心这个链表是对称的,举个例子比如12321和1221这两个都是回文,如果是123123这个还是回文吗,这种就不是回文了

思路:我们可以先找中间节点,找中间节点有什么好处呢,当我们判断链表是否是回文链表,是不是首先寻找中间节点,中间节点断开然后再从两头对应,要是两头节点的值都能一一对应,我们就说这个链表是回文链表,我们找到中间节点之后,然后从中间节点断开,将中间节点后面的链表反转,这样就可以一一比较,怎么比呢,一个从头节点开始,另一个从中间节点开始,一一比较,就可以了,都相等就是回文链表,只要有一个不相等,就不是回文链表

现在我们思考一个问题,需要分情况吗,很简单,画个图就知道了

在这里插入图片描述

代码实现:

 struct ListNode* middleNode(struct ListNode* head) {struct ListNode* fast = head,*slow = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}return slow;
}struct ListNode* reverseList(struct ListNode* head) {if(head == NULL){return NULL;}struct ListNode* n1 = NULL,*n2 = head,*n3 = n2->next;while(n2){n2->next = n1;n1 = n2;n2 = n3;if(n3)n3 = n3->next;}return n1;
}
bool isPalindrome(struct ListNode* head) {struct ListNode* mid = middleNode(head);struct ListNode* rmid = reverseList(mid);struct ListNode* cur = head; while(rmid){if(cur->val != rmid->val){return false;break;}cur = cur->next;rmid = rmid->next;}return true;
}

5. 相交链表

OJ链接:160. 相交链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:我们首先要有一个共识就是,链表相等不一定相交,这个大家一定要清楚这一点,我们认为如果两个链表相交,那么他们的地址是相同的,而不是数据是相等的,像这样,我们需要注意一下

在这里插入图片描述

我们可以一个一个节点进行比较,将链表A的所有节点和链表B的所有节点,但是我们想想时间复杂度是多少,是不是O(n*2),不满足题意

思路二:首先我们要做的是判断两个链表是否相交,再找出交点,我们大致的思路就是这样,那我们应该怎么判断呢,怎么找呢

  • 哎,我们把两个链表都遍历一下,如果它们的尾节点指向的位置相同是不是两个链表就一定会相交,这就解决了如何判断两个链表是否会相交的问题
  • 那我们知道两个链表相交之后,如何找交点呢,我们想一想如果我们让两个指针都指向头节点,然后再同时遍历的话,是不是有可能会错过啊,就像这样

在这里插入图片描述

  • 是不是啊,说的没有问题吧,那怎么办呢,看来大家都很聪明,已经发现了,如果我们让 pb 先走一步,让它们的起始位置都一样,然后再让它们一步一步地走就可以相遇了,像这样

在这里插入图片描述

哦,也就是说,我们先让长一些指针走,走它们的差值步,使两个指针指向的位置相同,再让两个指针一步一步往下走,就可以找到交点了,这个思路还是很清晰的,接下来就是代码实现了

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode* pa = headA,*pb = headB;int lenA = 1,lenB = 1;while(pa){pa = pa->next;lenA++;}while(pb){pb = pb->next;lenB++;}if(pa != pb){return NULL;}int gap = abs(lenA - lenB);//假设法struct ListNode* plong = headA,*pshort = headB;if(lenA < lenB){plong = headB;pshort = headA;}while(gap--){plong = plong->next;}while(plong != pshort){plong = plong->next;pshort = pshort->next;}return plong;
}

6. 带环链表

OJ链接:141. 环形链表 - 力扣(LeetCode)

在这里插入图片描述

思路:主要的思路还是我们的快慢指针,也就是让慢指针走一步,快指针走两步,先让两个指针从头节点的位置出发如果带环的话,一定会在环中相遇,否则的话,快指针就先指向尾节点,画个图就能更好的理解

在这里插入图片描述

理顺思路之后,代码就很好写的

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

好了,这个题还是非常简单的,那么在我们面试的时候,面试官可能会问我们两个问题

6.1 为什么一定会相遇,有没有可能会错过,或者出现永远追不上的情况,请证明

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

在这里插入图片描述

6.2 slow一次走一步,fast如果一次走3步,走4步,走5步还能追上吗,请证明

证明:我们假设 fast 一次走3步,当 slow 进环时,和 fast 距离是N,追击过程距离变化如下图所示:

在这里插入图片描述

我们可以很明显的看到,当N为偶数时,就能追击上,当N为奇数时,最后值为-1就表明已经错过了,要开始进行新一轮的追击

接下来我们在考虑新一轮追击距离变成了多少,是不是C-1(假设C为环的长度)

我们想一想是不是也要分两种情况当C-1为偶数时,就能追击上,当C-1为奇数时,最后值还是为-1,就永远追不上

小结一下:

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

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

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

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

那我们在思考一下,b成立的条件,是不是永远就追不上呢,我们发现当N为奇数且C为偶数时,结论成立,我们刚在是逻辑上想的,接下来证明一下,看看是不是就永远追不上

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

假设slow进环时,fast已经走了x圈

设slow走过的距离为 L

fast走过的距离就是 L+x*C+C-N

那么我们又说了,fast走的距离是slow距离的3倍

就可以非常轻松地写出 3L = L+x*C+C-N

化简一下就是 *2L = (x-1)C-N

哦,这就非常明显了,等式左边一定是偶数,那就说明等式右边也一定是偶数

  • 我们说当N为偶数的话,那么C只能为偶数,(x-1)*C才为偶数
  • 当N为奇数的话,那么C只能为奇数,只有奇数-奇数才能是偶数

那就说明当N为奇数且C为偶数时,条件不成立,所以永远追不上的假设不成立,一定会追上的

结论:

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

7. 带环链表2

OJ链接:142. 环形链表 II - 力扣(LeetCode)

在这里插入图片描述

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

思路很简单,看看代码:

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

通常这个时候面试官会问一个问题,这个问题和我们一样

7.1 为什么它们最终肯定会在入口点的位置相遇,请证明

证明:假设slow和fast已经相遇了,设初始位置到入口点的距离为 L

设入口点到相遇点的距离为 N ,环的长度为 C

假设slow相遇时,fast已经走了x圈

那么fast走过的距离为 L+x*C+N

slow走过的距离为 L+N

思考一个问题我们slow与fast相遇的时候,slow一定走不完一圈

很简单,如果slow走完一圈的话,fast是不是就走完两圈了,是不是早就可以相遇了

fast走过的距离是slow走过距离的两倍

也就是 **L+x * C+N = 2 * (L+N) **

化简一下也就是 *L = (x-1)C + C-N

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

对大家来说这些都很EZ,对不对,接下来的最后一道OJ题就是比较综合的了

8. 复制链表

OJ链接:138. 随机链表的复制 - 力扣(LeetCode)

在这里插入图片描述

题干中提到了一个深拷贝的概念,深拷贝是拷贝一个值和指针指向跟当前链表一模一样的链表

思路一:这道链表题每个节点里多了个指针指向随机节点,也有可能指向空,然后深拷贝一份,如果我们直接遍历然后拷贝呢?硬拷贝是可以的,但是有个问题,随机指针(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;
}

结语

好了,感谢你能看到这里,本篇到这就结束了,希望对你有所帮助

溜了溜了,我们下篇文章再见吧

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

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

相关文章

Jmeter如何进行分布式测试

使用Jmeter进行性能测试时&#xff0c;有些同学问我如果并发数比较大(比如最近项目需要支持1000并发)&#xff0c;单台电脑的配置(CPU和内存)可能无法支持&#xff0c;怎么办就需要使用分布式压测 1.分布式原理&#xff1a; 1、Jmeter分布式测试时&#xff0c;选择其中一台作…

Selenium IED-控制已打开的Chrome浏览器

本文已收录于专栏 《自动化测试》 目录 背景介绍优势特点操作步骤总结提升 背景介绍 在我们进行自动化测试的过程中有时候会遇见一个很棘手的问题那就是登录的过程中需要图片验证码&#xff0c;图片验证码设计的初衷其实就是为了防自动化&#xff0c;防止一些人利用自动工具恶意…

缓冲区设置

缓冲区设计 一、简介 在网络通讯中&#xff0c;用户态缓冲区和内核态缓冲区的大小设定对于优化网络性能和确保数据传输可靠性至关重要。下图是网路通讯的内核缓冲区使用情况&#xff1a; 数据的读写都需要进行系统调用&#xff0c;从用户态切换到内核态去接收数据&#xff0…

昂科烧录器支持TI德州仪器的超低功耗微控制器MSP430F2013IRSAR

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中TI德州仪器的超低功耗微控制器MSP430F2013IRSAR已经被昂科的通用烧录平台AP8000所支持。 MSP430F2013IRSAR超低功耗微控制器由多种设备组成&#xff0c;这些设备具有针对各种应…

Shellcode详解

Shellcode详解 一、Shellcode的特点二、Shellcode的类型三、Shellcode的工作原理四、防御措施五、常见的PHP Web Shell示例5.1 简单的命令执行5.2 更复杂的Web Shell5.3 防御措施5.4 实际案例 Shellcode是一种小巧、紧凑的机器代码&#xff0c;通常用于利用软件漏洞或注入攻击中…

集体爆雷!突发中科院2区(Top) 被标记!新增10本期刊被“On Hold“

本周投稿推荐 SSCI • 中科院2区&#xff0c;6.0-7.0&#xff08;录用友好&#xff09; EI • 各领域沾边均可&#xff08;2天录用&#xff09; CNKI • 7天录用-检索&#xff08;急录友好&#xff09; SCI&EI • 4区生物医学类&#xff0c;0.5-1.0&#xff08;录用…

Nginx缓存之web缓存配置

Web 缓存可节约网络带宽&#xff0c;有效提高用户打开网站的速度。由于应用服务器被请求次数的降低&#xff0c;也相对使它的稳定性得到了提升。Web 缓存从数据内容传输的方向分为前向位置缓存和反向位置缓存两类。如下图所示。 前向位置缓存既可以是用户的客户端浏览器&#x…

处理耗时任务

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 namespace 处理耗时任务 {public partial class Form1 : Form{public Form1(){InitializeComponent();}bool IsRun false;private string path Directory.GetCurrentDirectory() "\\古诗词.txt";private…

Vite+Vue3安装且自动按需引入Element Plus组件库

一&#xff0c;安装Element Plus npm install element-plus //node环境16二&#xff0c;安装插件 npm install unplugin-auto-import unplugin-vue-components -D三&#xff0c;配置vite.config.ts文件 //按需引入element-plus组件 import AutoImport from unplugin-auto-i…

stm32 下载程序只能使用串口1

stm32单片机用串口下载程序时候&#xff0c; 默认只能用 UART1&#xff0c;其他几个USART是都不行的。 因为在固件中ISP自举程序用的是UASRT1&#xff0c;如果想用其他串口下载程序需要修改bootloader 在硬件设计时一定要注意&#xff01;切记。 自己买回来的stm32片子只能用…

Oracle基本语法

前言&#xff1a; 1.使用的数据库不同&#xff0c;所使用的语法也略有不同 2.SQL对大小写不敏感&#xff0c;无论大小写&#xff0c;sql自动转换为大写 3.用户名、表名、表空间名、文件路径......等需要用单引号将其包含 4.一般引号里面的内容需要大写 准备工作&#xff1a; &a…

0618_QT4

练习&#xff1a; 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和…

cpp混淆详解

cpp混淆详解 一、常见的代码混淆技术二、自动化混淆工具三、示例&#xff1a;使用宏定义进行混淆四、注意事项 C代码混淆是一种技术&#xff0c;旨在通过修改代码的结构和符号名称&#xff0c;使其难以被人类阅读和理解&#xff0c;同时保持代码的功能和性能不变。这种技术常用…

安装和配置JFrog Artifactory

1、安装JFrog Artifactory 下载安装包 : ubuntu系统: wget -O artifactory-pro.deb "https://releases.jfrog.io/artifactory/artifactory-pro-debs/pool/jfrog-artifactory-pro/jfrog-artifactory-pro-[RELEASE].deb" centos系统: wget -O artifactory-pro…

反弹shell

反弹shell&#xff1a; 简介; ​ 反弹shell&#xff0c;就是攻击机监听在某个TCP/UDP端口为服务端&#xff0c;目标机主动发起请求到攻击机监听的端口&#xff0c;并将其命令行的输入输出转到攻击机。 正向连接&#xff1a; ​ 假设我们攻击了一台机器&#xff0c;打开了该…

性能工具之 JMeter 常用组件介绍(八)

文章目录 一、Jmeter命令行启动二、Jmeter脚本录制 一、Jmeter命令行启动 Jmeter有两种运行&#xff1a; 一种是采用的界面模式(GUI&#xff09;启动&#xff0c;会占用不少系统资源&#xff1b;另一种是命令行模式&#xff08;non-GUI&#xff09;执行&#xff0c;这样节约资…

《SelectDB 新一代日志存储分析平台解决方案》白皮书重磅发布|立即下载

随着信息技术的飞速进步&#xff0c;企业面临着前所未有的系统复杂性和数据挑战。在此背景下&#xff0c;日志数据成为了企业洞察系统内部状态、监控网络安全以及分析业务动态的宝贵资源&#xff0c;构建高效的日志存储与分析平台至关重要。 作为基于 Apache Doris 打造的现代…

使用 AST语法树分析与修改Javascript 代码

1、AST语法树简介 当编写代码分析工具、代码美化工具、网站逆向分析等场景时&#xff0c;通常需要使用AST语法树技术。 比如项目开发过程中常遇到的场景&#xff1a;某个公共函数名需要更改&#xff0c;但被很多文件多处代码调用&#xff0c;手工修改非常容易漏改、改错等&…

【漏洞复现】致远互联FE协作办公平台 ncsubjass SQL注入

0x01 产品简介 致远互联FE协作办公平台是一款为企业提供全方位协同办公解决方案的产品。它集成了多个功能模块&#xff0c;旨在帮助企业实现高效的团队协作、信息共享和文档管理。 0x02 漏洞概述 致远互联FE协作办公平台 ncsubjass.jsp接口处存在SQL注入漏洞,未经身份验证的…

【Linux】环境设置MySQL表名忽略大小写

目录 说明 一、摘要 二、查看服务器上MySQL情况 方式一&#xff1a;通过Linux方式 方式二&#xff1a;借助可视化工具&#xff08;Navicat&#xff09; 三、MySQL设置忽略表名大小写的参数&#xff08;lower_case_table_names&#xff09; 四、网上解决方案 方法一&…