环形链表 [两道题目](详解)

2024-05-04 04:04

本文主要是介绍环形链表 [两道题目](详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

环形链表(详解)

第一题:

题目:

题目链接

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

思路:

首先我们创建一个快慢指针,让其在链表内不断走,如果链表是带环的,那么快指针迟早会追上慢指针,追上了那就是带环链表。

但是注意了在这里

我们的快慢指针之间的速度差距一定得是——fast = 2slow,即是慢指针走1步,快指针走两步。

为什么呢?

我们假设slow指针进入环时离fast指针的距离是X。

  1. 当X == 0的时候,slow一进环就碰到fast了,说明第一个节点到环的长度和环内的长度是一样的。
  2. 如果slow走到环的长度是,环内的一半长度,这个时候,slow进环的时候,fast刚好在环中间。如图所示

image-20240503162211058

  1. 也就是说,当slow慢指针入环之后,fast和slow每走一次,他们之间的距离X都会-1.如图所示

image-20240503162320737

其实就证明了,slow入环之后,和fast的距离是X,slow再走X次,fast就能追上slow指针。

那为什么说fast只能是slow的两倍速率呢?

因为如果我们的fast走其他步 比如3步 4步,就有可能会不相遇!

image-20240503163459651

如图所示,如果X是奇数,比如是1,那么一次走三步就会直接跳过去,那么此时的X就会重置为环的长度-1,如果这个新的X也是奇数,那么就会死循环!

fast走其他步也是同样的道理,这里就不在例举。

因此其他步总有一些特殊的情况无法处理,无法相遇。

但是如果fast每次走两步,每次的距离减少1 那就一定会相遇。

因此这里只能让fast = 2slow。

慢指针在环内是走不到一圈的,严格来说,最差的情况是slow在入环第一个节点,fast在入环第二个节点,这个时候慢指针走接近2/3圈就会被追上。

代码实现:

struct ListNode 
{int val;struct ListNode* next;
};typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head)
{// 创建快慢指针ListNode* slow, * fast;slow = fast = head;// 快慢指针都在环里面一直走下去,快指针迟早能追上慢的指针while (fast && fast->next) // 我们不知道传进来的链表是不是带环的因此,fast和fast->next 都不能为NULL{slow = slow->next;fast = fast->next->next; // 注意了这里的快指针只能是 fast = 2slow // 如果快指针追上慢指针那就是带环if (fast == slow)return true;}// 走到这里说明 不带环 return false;
}

第二题:(难度:中等)

题目:

题目链接

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

思路:

思路1:(理解容易,写起来较麻烦)
  1. 创建快慢指针,如果有环就相遇,无环无法相遇
  2. 首先让fast和slow相遇,找到这个相遇的节点。
  3. 然后把相遇的节点断开
  4. 然后相遇点和到相遇点的上一个节点是一个链表,一开始的链表头到断点也是一个链表。
  5. 这个时候我们把问题转化成了两个链表的相交问题,找到交点,这个交点就是题目要找的入环的第一个节点!

image-20240503182909788

代码实现:
struct ListNode 
{int val;struct ListNode* next;
};typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) 
{// 排除空链表的情况if(head == NULL)return NULL;// 创建快慢指针ListNode* slow, *fast;slow = fast = head;// 让快慢指针遍历链表,看看带环while(fast && fast->next){slow = slow->next;fast = fast->next->next;// 判断是否有相遇点if(slow == fast)break;}// 排除不带环的情况if(slow != fast)return NULL;// 让相遇点断开 ListNode* next = slow->next; // 让next存储相遇点的下一个节点slow->next = NULL; // 断开相遇点// 这个时候next 和 head 分别为两条链表// 这个时候我们就找这两个链表的相交点就好了,交点就是入环第一个节点// 要想找到交点首先要求两个链表的长度ListNode* curhead = head;ListNode* curnext = next;int lenhead = 0;int lennext = 0;// 遍历head链表,统计长度while(curhead){lenhead++;curhead = curhead->next;}// 统计next链表长度while(curnext){lennext++;curnext = curnext->next;}// 默认head为起始点的为长链表 next为起始点的为短链表ListNode* longlist = head;ListNode* shortlist = next;// 判断两个链表的长度是否和我们默认的情况相同if(lenhead < lennext){   // 不同就改过来longlist = next;shortlist = head;}// 计算两个链表的长度差int gap = abs(lenhead - lennext);// 让长链表走两个链表的长度差while(gap--){longlist = longlist->next;}// 这个时候长短链表的长度相同了while(longlist){// 这个时候要判断next指向的是否是入环的第一个节点,因为快慢指针的相遇点可能是入环的最后一个节点// 因此要先判断是否为交点if(longlist == shortlist)return longlist;// 让两个链表都向后走longlist = longlist->next;shortlist = shortlist->next;}// 如果走到这里就说明有情况没有排除// 比如链表只有一个节点且不带环return NULL;
}
思路2:(理解较难,写起来比较容易)

注意 思路2一定要知道推论是怎么推出来的,要理解透彻代码是怎么来的!

  1. 首先找通过快慢指针找到相遇点。

  2. 我们假设第一个节点到入环的第一个节点的距离叫做L,把slow指针入环后走的距离叫X,环的长度是C。如图所示

  3. image-20240503200031822

  4. 那么慢指针走的距离就是:L + X (慢指针在环内一定走不过一圈)

  5. 快指针走的距离就是: L + N * C + X (N代表走的环的圈数)

如果L短C大 也就是环的长度大 那么N就小,就是快指针绕环走的圈数少

如果L长C小,也就是环的长度小,N就大,就是快指针要绕环走很多圈,慢指针才能入环。

image-20240503200737642

如图所示,L长C小的情况,N会比较大

  1. 我们又知道 2slow = fast 也就是2(L + X) = L + N*C + X
  2. 推论得知 L = N*C - X. 也就是第一个节点到入环的第一个节点的距离L = 快指针在环内走的长度N * C 再减去一个慢指针入环的距离 X

也可以理解成 L = (N-1) C + C - X*

也就是fast走了(N-1)* C 的距离后再走了一个 C - X的距离就是L的长度

image-20240503201618422

  1. 也就是说从相遇点有一个指针,和链表的起始点开始一起走,两个指针相遇的节点就是我们要找的入环的第一个节点
代码实现:
struct ListNode 
{int val;struct ListNode* next;
};typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) 
{// 首先创建快慢指针ListNode* slow, *fast;slow = fast = head;// 通过快慢指针判断是否带环,并且让slow和fast相遇时就退出while(fast && fast->next){slow = slow->next;fast = fast->next->next;// 如果相遇就退出if(slow == fast)break;}// 走到这里有可能时slow == fast 也有可能链表不是带环链表// 传入空链表的情况也要排除// 如果链表只有一个节点且不自身成环的情况要排除if(fast == NULL || fast->next == NULL) // 排除不是带环链表的情况return NULL;// 让相遇点和链表起始点同时开始走,当两个指针相遇的时候,这个节点就是入环的第一个节点// 注意!这里是一个推论,切记不可不知道推论怎么来的! // 推论详解  看自己的笔记或者博客。博客地址:ListNode* meet = slow;while(meet != head){meet = meet->next;head = head->next;}// 走到这里meet和head指向的节点就是入环的第一个节点return meet;
}

这篇关于环形链表 [两道题目](详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/958298

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

题目1254:N皇后问题

题目1254:N皇后问题 时间限制:1 秒 内存限制:128 兆 特殊判题:否 题目描述: N皇后问题,即在N*N的方格棋盘内放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在同一斜线上。因为皇后可以直走,横走和斜走如下图)。 你的任务是,对于给定的N,求出有多少种合法的放置方法。输出N皇后问题所有不同的摆放情况个数。 输入

题目1380:lucky number

题目1380:lucky number 时间限制:3 秒 内存限制:3 兆 特殊判题:否 提交:2839 解决:300 题目描述: 每个人有自己的lucky number,小A也一样。不过他的lucky number定义不一样。他认为一个序列中某些数出现的次数为n的话,都是他的lucky number。但是,现在这个序列很大,他无法快速找到所有lucky number。既然

建立升序链表

题目1181:遍历链表 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2744 解决:1186 题目描述: 建立一个升序链表并遍历输出。 输入: 输入的每个案例中第一行包括1个整数:n(1<=n<=1000),接下来的一行包括n个整数。 输出: 可能有多组测试数据,对于每组数据, 将n个整数建立升序链表,之后遍历链表并输出。 样例输

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor