链表经典OJ问题【环形链表】

题目导入

题目一:给你一个链表的头节点 head ,判断链表中是否有环
题目二:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL。

题目一

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

分析

这里我们不能用单个指针来判断是否带环,这样是行不通的,因为我们不知道结束条件是什么;可能有人会想“让单个指针与带环链表的入口进行对比”,但是这有个问题,我们并不知道哪个节点是这个环形链表的节点。
类似下图
在这里插入图片描述

尾节点的next指向哪里是不确定的,有可能指向头节点,也有可能指向其他节点(极端情况指向自己),还有可能就不是一个环形链表(指向NULL)。

所以这里要使用快慢指针(这是一个很棒的解题思路),在这个题我们就让慢指针走一步,快指针走两步。
具体代码如下

struct ListNode* slow = head , *fast = head;
slow = slow->next;//慢指针走一步
fast = fast->next->next;//快指针走两步

这个指针不可能只会走一次的,所以是需要循环来完成的

bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head , *fast = head;
    while()
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return false;//链表不为环形链表
    
}

这里的结束条件是什么呢?
这里是判断该链表是否为环形链表,所以我们的判断条件是fast != NULL && fast->next != NULL
这里为什么要判断fast->next呢,假设这条链表不是环形链表,且fast->next是指向NULL,如果我们不对 fast->next进行判断的话,进入循环就会出现fast->next已经为空,我们还对他进行了解引用,这样就会报错
所以这里的结束条件为

while(fast && fast->next)
{
	…………//代码段
}

好了,循环已经写好了,那么来写结束条件吧(判断是否带环),不能说因为代码死循环了就说链表是环形链表吧。
当slow等于fast时就表示该链表为环形链表。
为什么呢?
我们借图来了解:

开始将head赋予给slow和fast开始将head赋予给slow和fast
因为fast指针的行走速度是slow指针的两倍,所以当slow走了链表的中间节点时,fast就已经走到尾节点了。
在这里插入图片描述

slow走到尾节点时,就代表slow也进环了,这时候就成了追击问题,当fast == slow时,就代表该链表是环形链表。
完整代码如下:

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. 如果我的fast走的不是两步,而是三步,四步或者更多呢?

衍生问题一

要证明这个很简单。
slow走到尾节点时,就代表slow也进环了,但这时候的fast的位置是不确定(可能已经转了好多圈了),所以我们假设fastslow的距离为N
如图:
在这里插入图片描述
因为fast是slow的两倍,所以追击一次,他俩之间的距离就会减一,一直追击下去距离就会一直减一,直到为0,也就是他两相等了

距离:N -> N-1 -> N-2 -> ……-> 2 -> 1 -> 0

因为fast是slow的两倍,所以每追击一次,他们的距离就会减一,所以他们两个不会错过。

衍生问题二

我们拿fast一次走三步来举例(其他的证明过程都大差不多,无非就是更复杂了)。
这时fast的速度就是slow的三倍,这时每追击一次,距离就会减二,这时候就要考虑两个情况了

  • 情况一:他们两个之间的距离为偶数
    这很简单,因为N为偶数,所以N%2=0;所以他们一定会在第一轮相遇。
  • 情况二:距离为奇数
    这样追击下去,当他们之间的距离为1时,在追击一次后,fast就会跑到slow的前面,开启新一轮的追击

N为奇数: N -> N-2 -> N-4 -> …… -> 5 -> 3 -> 1 -> -1

假设这个环形链表的长度为C。
这时候又要看两种情况这个C-1是否为偶数;
当C-1为偶数时,那么就和情况一一样,就一定会相遇。
如果C-1还是奇数,那就真的永远都遇不到了。

那么真的会出现N为奇数,C为偶数(C-1为奇)的情况吗?
其实是不可能的,我们将他们进行换算就可以知道为什么了。
在这里插入图片描述

总结论:使用快慢指针,环形链表一定会相遇,如果N为偶数,那么C一定为偶数;N为奇数,C一定为奇数。

题目二

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

这里就是在判断环形链表的基础上再加一些要求,那前置的代码,可以直接使用上面的代码

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)//相遇了
        {
			
        }
    }
    return NULL;//无环
}

那既让相遇了,我们肯定就是在相遇之后进行操作,也就是在if语句里写代码。
既让已经相遇了,那么slow的步数就为L+N(slow在环内走了 N 步),fast的步数就为L + x*C + N(走了一圈x就加1,然后因为slow在环内走了 N 步,使用就为x*C+N)。
我们将slowfast相遇时的节点,给到meet

        if(slow == fast)//相遇了
        {
			struct ListNode* meet = 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;//走到这里就代表meet == head
        }
    }
    return NULL;
}

其实这两题本意都不难,难的是衍生问题和背后的数学逻辑(其实拆开了也不难),所以这也成了以前面试时会考的点,
考察的是你的逻辑思维。

结语

最后感谢您能阅读完此片文章,如果有任何建议或纠正欢迎在评论区留言,也可以前往我的主页看更多好文哦(点击此处跳转到主页)。
如果您认为这篇文章对您有所收获,点一个小小的赞就是我创作的巨大动力,谢谢!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CTF Web】CTFShow web3 Writeup(SQL注入+PHP+UNION注入)

web3 1 管理员被狠狠的教育了&#xff0c;所以决定好好修复一番。这次没问题了。 解法 注意到&#xff1a; <!-- flag in id 1000 -->但是拦截很多种字符。 if(preg_match("/or|\-|\\|\*|\<|\>|\!|x|hex|\/i",$id)){die("id error"); }使用…

Hadoop+Spark大数据技术 实验7 Spark综合编程

删除字符串 package HelloPackageimport scala.io.StdInobject DeleteStr {def main(args: Array[String]): Unit {var str1 ""var str2 ""var i 0var j 0var flag 0print("请输入第一个字符串:")str1 StdIn.readLine()print("请输…

Docker简单使用

1.简单认识 软件的打包技术&#xff0c;就是将打乱的多个文件打包为一个整体&#xff0c;比如想使用nginx&#xff0c;需要先有一台linux的虚拟机&#xff0c;然后在虚拟机上安装nginx.比如虚拟机大小1G&#xff0c;nginx100M。当有了docker后我们可以下载nginx 的镜像文件&am…

Excel/WPS《超级处理器》同类项处理,合并同类项与拆分同类项目

在工作中处理表格数据&#xff0c;经常会遇到同类项处理的问题&#xff0c;合并同类项或者拆分同类项&#xff0c;接下来介绍使用超级处理器工具如何完成。 合并同类项 将同一列中的相同内容合并为一个单元格。 1&#xff09;用分隔符号隔开 将AB列表格&#xff0c;合并后为…

chrome125.0.6422.60驱动包下载

百度网盘地址:https://pan.baidu.com/s/1DAr_O58GQ6m4sk_QePZscA?pwd=5t0j 提取码:5t0j Chrome驱动包(ChromeDriver)是一个用于支持自动化测试的工具,它提供了对Google Chrome浏览器的控制,使您可以编写和运行自动化脚本来测试网站。这个驱动程序是由Selenium项目开…

Web安全:SQL注入之时间盲注原理+步骤+实战操作

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

【Python】 Python脚本中的#!(Shebang):使用指南与最佳实践

基本原理 在Python脚本编程中&#xff0c;#!&#xff08;通常称为shebang&#xff09;是一个特殊的行&#xff0c;它告诉操作系统使用哪个解释器来执行脚本。在Unix-like系统中&#xff0c;shebang是必需的&#xff0c;因为它允许脚本作为独立的程序运行&#xff0c;而不需要显…

mysql 、oss 结合使用

以下是一个使用 Express、MySQL、OSS 和 axios 的 Node.js 示例。这个示例创建了一个 Express 服务器&#xff0c;该服务器有一个路由用于处理视频上传的请求。视频文件首先被上传到 OSS&#xff0c;然后视频的 OSS URL 被存储到 MySQL 数据库。 首先&#xff0c;我们需要安装必…

mysql实战——xtrabackup全量备份/增量备份及恢复

一、测试前准备 mysql数据库 端口3306数据文件目录 /data/mysql/3306/data 安装目录/usr/lcoal/mysql配置文件/etc/my.cnf 创建数据库 testXtra 创建备份目录 备份目录/data/backup/备份恢复数据文件目录/data/mysql/3307/data备份恢复配置文件/etc/my_3307.cnf 二、开始…

【spring】@ResponseBody注解学习

ResponseBody介绍 ResponseBody 是一个Spring框架中的注解&#xff0c;主要用于Web开发&#xff0c;特别是在Spring MVC框架中。它的核心作用是改变Spring MVC处理HTTP请求响应的行为&#xff0c;使得从控制器方法返回的数据直接写入HTTP响应体&#xff08;Response Body&…

tomcat--安全配置多虚拟机

端口8005/tcp 安全配置管理 8005是Tomcat的管理端口&#xff0c;默认监听在127.0.0.1上。无需验证就可发送SHUTDOWN (大小写敏感)这个字符串&#xff0c;tomcat接收到后就会关闭此Server。此管理功能建议禁用&#xff0c;可将SHUTDOWN改为一串猜不出的字符串实现或者port修改成…

自己手写一个线性表List【C风格】

#include <iostream>//线性表、顺序表List#define MAX_SIZE 20 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0typedef int Status;//返回状态类型 typedef int ElemType;//元素类型//结构体 typedef struct {ElemType data[MAX_SIZE];//数据类型&#x…

探索 JavaScript 新增声明命令与解构赋值的魅力:从 ES5 迈向 ES6

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 &#x1f4af;声明命令 let、const&#x1f35f;1 let声明符&a…

Linux程序开发(十一):进程与进程间通信设计之趣味猫咪抓老鼠游戏

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

Java堆栈分区

Java在内存分配区域&#xff1a;栈区&#xff08;stack&#xff09;、堆区&#xff08;heap&#xff09;、方法区&#xff08;Method Area&#xff09;、常量池。 一、栈区 每个方法&#xff08;Method&#xff09;执行时&#xff0c;都会创建一个方法栈区。用于存储局部变量表…

【FreeRTOS移植到STM32F103C8T6超详细教程-->>>基于标准库】

移植教程 FreeRTOS简介FreeRTOS 介绍FreeRTOS优点 FreeRTOS移植FreeRTOS 下载FreeRTOS目录结构移植开始Keil5打开工程修改FreeRTOSConfig.h文件修改stm32f10x_it.c 测试FreeRTOS闪烁第一颗小灯 FreeRTOS简介 FreeRTOS 介绍 FreeRTOS 是市场领先的面向微控制器和小型微处理器的…

linux 安装chrome浏览器

一、下载安装包 下载地址&#xff1a;https://download.csdn.net/download/k0307x1990y/89349171 二、安装流程 [rootlocalhost ~]# rpm -ivh *.rpm [rootlocalhost ~]# yum -y localinstall google-chrome-stable_current_x86_64.rpm [rootlocalhost ~]# 三、修改配置文件…

Web(数字媒体)期末作业

一.前言 1.本资源为类似于打飞机的网页游戏 2.链接如下&#xff1a;【免费】前端web或者数字媒体的期末作业&#xff08;类似于打飞机的2D网页小游戏&#xff09;资源-CSDN文库 二.介绍文档

不靠后端,前端也能搞定接口!

嘿&#xff0c;前端开发达人们&#xff01;有个超酷的消息要告诉你们&#xff1a;MemFire Cloud来袭啦&#xff01;这个神奇的东东让你们不用依赖后端小伙伴们&#xff0c;也能妥妥地搞定 API 接口。是不是觉得有点不可思议&#xff1f;但是事实就是这样&#xff0c;让我们一起…

跨境电商赛道,云手机到底能不能化繁为简?

当下国内电商背景&#xff1a; 从零售额的数据来看&#xff1a;随着互联网的普及和消费者购物习惯的改变&#xff0c;国内电商市场规模持续扩大。据相关数据显示&#xff0c;网络消费亮点纷呈&#xff0c;一季度全国网上零售额达到了3.3万亿元&#xff0c;同比增长12.4%。这表…