环形链表

解题思路

  1. 定义两个指针,一个快指针,一个慢指针,快指针每次移动两个节点,慢指针每次移动一个节点。
  2. 从头节点开始,让快慢指针同时移动,如果链表中有环,那么快慢指针一定会在某个节点相遇。
  3. 如果快慢指针相遇了,说明链表中有环,返回true。如果快指针移动到了null,说明链表中没有环,返回false。

思考1:为什么快慢指针能判断是否有环

  • 如果链表中没有环,那么快指针会先于慢指针到达链表的尾部,也就是null,这时候我们就可以判断链表中没有环,返回false。
  • 如果链表中有环,那么快指针会在环中不断地追赶慢指针,就像两个人在环形跑道上跑步,速度快的人总会追上速度慢的人。这时候,快指针和慢指针一定会在某个节点相遇,这时候我们就可以判断链表中有环,返回true。
  • 为什么快指针要每次移动两个节点,慢指针要每次移动一个节点呢?这是为了保证快指针和慢指针的相对速度是一定的,如果快指针每次移动三个节点,慢指针每次移动一个节点,那么快指针的速度就是慢指针的三倍,这样可能会导致快指针在环中跳过慢指针,从而无法相遇。如果快指针每次移动一个节点,慢指针每次移动一个节点,那么快指针和慢指针的速度就是一样的,这样也无法相遇。所以,快指针每次移动两个节点,慢指针每次移动一个节点,是一种比较合理的选择,可以保证快慢指针在环中一定会相遇。

代码实现

// 定义一个判断链表是否有环的方法,接受一个头节点作为参数
public boolean hasCycle(Node head) {
// 如果头节点为空,直接返回false
if (head == null) {
return false;
}
// 定义快慢指针,初始化为头节点
Node fast = head;
Node slow = head;
// 用一个循环来移动快慢指针
while (fast != null && fast.next != null) {
// 快指针每次移动两个节点
fast = fast.next.next;
// 慢指针每次移动一个节点
slow = slow.next;
// 如果快慢指针相遇了,说明有环,返回true
if (fast == slow) {
return true;
}
}
// 如果快指针移动到了null,说明没有环,返回false
return false;
}

环形链表II

解题思路

  1. 定义两个指针,一个快指针fast,一个慢指针slow,都从头节点head开始遍历链表。
  2. 快指针每次走两步,慢指针每次走一步,如果链表有环,那么快慢指针一定会在环内相遇,否则快指针会先遍历完链表(遇到null)。
  3. 如果快慢指针相遇,说明链表有环,此时将快指针重新指向头节点head,慢指针保持在相遇节点,然后快慢指针都每次走一步,直到再次相遇,相遇的节点就是入环的第一个节点。即:从头节点到入环节点的距离,等于从相遇节点继续走到入环节点的距离。
  4. 如果快慢指针没有相遇,说明链表无环,返回null。

举个例子:

  1 -> 2 -> 3 -> 4
^ |
| v
7 <- 6 <- 5
  • 这个链表中有一个环,从节点2到节点7。环的长度是6,入口节点是节点2。
  • 我们可以用快慢指针的方法,让快指针从节点1开始,每次走两步,慢指针从节点1开始,每次走一步,它们在节点7相遇,然后让快指针从头开始一步步走,慢指针保持在相遇节点开始一步步走,快慢指针再次在2节点相遇,所以2节点就是入环节点。

思考1:为什么慢指针每次移动一步,快指针每次移动两步,一定会在环中相遇而不是错过?

假设链表的长度是L,环的长度是n,环的入口节点距离头节点的距离是a,快慢指针在环中相遇的节点距离环的入口节点的距离是b,那么有以下关系:

  • 当快慢指针相遇时,慢指针走过的距离是a+b,快指针走过的距离是a+b+k*n,其中k是快指针在环中绕的圈数。
  • 因为快指针的速度是慢指针的两倍,所以快指针走过的距离是慢指针的两倍,即2*(a+b) = a+b+kn,化简得a+b = kn。
  • 这个式子的意思是,当快慢指针相遇时,慢指针走过的距离刚好是环的长度的整数倍,也就是说,慢指针在环中走了k圈,快指针在环中走了k+1圈。
  • 这样,我们可以知道,快慢指针一定会在环中相遇,而不是错过,因为快指针每次都会比慢指针多走一步,所以它们之间的距离每次都会缩小一步,直到相遇为止。

思考2:如何证明,从头节点到入环节点的距离,等于从相遇节点继续走到入环节点的距离?

假设从头结点到环形入口节点的距离为x。 环形入口节点到fast指针与slow指针相遇节点距离为y。从相遇节点再到环形入口节点距离为z

  • 当快慢指针首次相遇时,慢指针走过的路程为x + y,快指针走过的路程为x + y + n (y + z)。
  • 由于快指针的速度是慢指针的两倍,所以快指针走过的路程是慢指针的两倍,即(x + y) * 2 = x + y + n (y + z)。
  • 化简得到x + y = n (y + z),即x = n (y + z) - y,再从n(y+z)中提出一个(y+z)来,得到x = (n - 1) (y + z) + z。而 y + z正好是环一圈的长度。
  • 这个式子的意义是,从头节点到入环节点的距离,等于快慢指针相遇后,一个指针继续走n-1圈,再加上从相遇点到入环节点的距离。
  • 当n=1时,即某一指针只走了一圈,那么x = z,即从头节点到入环节点的距离,等于从相遇节点继续走到入环节点的距离。
  • 因此,如果我们让一个指针从头节点开始,另一个指针从相遇节点开始,每次都走一步,那么他们最终会在入环节点相遇,这样就找到了入环节点。

代码实现

    public ListNode detectCycle(ListNode head) {
//如果链表为空或只有一个节点,直接返回null
if (head == null || head.next == null) {
return null;
}
//定义快慢指针
ListNode fast = head;
ListNode slow = head;
//遍历链表,直到快指针遇到null或快慢指针相遇
while (fast != null && fast.next != null) {
//快指针走两步,慢指针走一步
fast = fast.next.next;
slow = slow.next;
//如果快慢指针相遇,说明有环
if (fast == slow) {
//将快指针重新指向头节点
fast = head;
//快慢指针都每次走一步,直到再次相遇
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
//返回相遇的节点,即入环的第一个节点
return fast;
}
}
//如果快指针遇到null,说明无环,返回null
return null;
}

环形链表I、II(含代码以及证明)的更多相关文章

  1. 代码随想录第四天| 24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、160.链表相交、142.环形链表II

    今天链表致死量 第一题 public static class ListNode { int val; ListNode next; ListNode() {} ListNode(int val) { ...

  2. LeetCode142 环形链表 II

    给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 说明:不允许修改给定的链表. 进阶:你是否可以不用额外空间解决此题? //章节 - 链表 //二.双指针技巧 //2.环 ...

  3. 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II

    [算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ...

  4. Leetcode 142.环形链表II

    环形链表II 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 说明:不允许修改给定的链表. 进阶:你是否可以不用额外空间解决此题? 链表头是X,环的第一个节点是Y,sl ...

  5. LeetCode 142:环形链表 II Linked List Cycle II

    给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 - ...

  6. Leetcode.142-Linked-list-cycle-ii(环形链表II)

    环形链表II 思路 https://www.cnblogs.com/springfor/p/3862125.html https://blog.csdn.net/u010292561/article/ ...

  7. LeetCode 142. 环形链表 II(Linked List Cycle II)

    142. 环形链表 II 142. Linked List Cycle II 题目描述 给定一个链表,返回链表开始入环的第一个节点.如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整 ...

  8. Java实现 LeetCode 142 环形链表 II(二)

    142. 环形链表 II 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 ...

  9. [LeetCode题解]142. 环形链表 II | 快慢指针

    解题思路 本题是在141. 环形链表基础上的拓展,如果存在环,要找出环的入口. 如何判断是否存在环,我们知道通过快慢指针,如果相遇就表示有环.那么如何找到入口呢? 如下图所示的链表: 当 fast 与 ...

  10. 【LeetCode】142. 环形链表 II

    142. 环形链表 II 知识点:链表:set:快慢指针 题目描述 给定一个链表,判断链表中是否有环. 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表 ...

随机推荐

  1. Jenkinsfile Pipeline 使用 SSH 连接

    前提 首先你需要将用到的 SSH 私钥保存到 Jenkins 的凭据中,这样你会获得一个 credentialId.这不是本文主要的内容,故不在此展开赘述,详情可参考官方文档:https://www. ...

  2. K8s集群环境搭建

    K8s集群环境搭建 1.环境规划 1.1 集群类型 Kubernetes集群大体上分为两类:一主多从和多主多从 一主多从:一台master节点和多台node节点,搭建简单,但是有单机故障风险,适用于测 ...

  3. GO语言内存操作指导—unsafe的使用

    在unsafe包里面,官方的说明是:A uintptr is an integer, not a reference.Converting a Pointer to a uintptr creates ...

  4. 在链表上实现 Partition 以及荷兰国旗问题

    在链表上实现 Partition 以及荷兰国旗问题 作者:Grey 原文地址: 博客园:在链表上实现 Partition 以及荷兰国旗问题 CSDN:在链表上实现 Partition 以及荷兰国旗问题 ...

  5. mysql-front连接mysql报错1251解决方法

    客户端不通过服务器的验证,建议升级mysql客户端 出现错误的原因是mysql版本问题,由于mysql8.0版本与mysql5.0版本加密方式不同,导致mysql-front无法通过验证. mysql ...

  6. 【终极解决办法】pyinstaller打包exe没有错误,运行exe提示Failed to execute script 'mainlmageWindows' due tounhandled exception: No module named 'docx'

    一.通过pyinstaller打包exe可执行文件,由于我的py是多个,所以要先生成spec文件,代码如下: pyi-makespec mainImageWindows.py 此时生产了一个mainI ...

  7. 爬了10000张NASA关于火星探索的图片,我发现了一个秘密

    前言 最近,我使用爬虫技术,爬取了美国航空航天局,也就是你电影里经常见到的 NASA, 火星探索的相关图片,有 10000 张吧. 嗯嗯,小事情,小事情. 完事儿之后,有点小激动,于是就有了这篇文章, ...

  8. SQL语句查询关键字 多表查询

    目录 SQL语句查询关键字 select from 编写顺序和查询数据 前期数据准备 编写SQL语句的小技巧 查询关键字之筛选 where 逻辑运算符 not and or between not b ...

  9. windowserver中PowerShell禁止脚本执行的解决方法

    最近工作中在上线项目的时候安装Exceptionless时,运行powershell脚本,发现报错: 报错提示:You cannot run this script on the current sy ...

  10. 9、IDEA回退Git版本

    转载自 在工作中有时候会要求我们将以前提交的代码新开一个分支,而把之前提交的分支回退到以前某个版本. 1.通过IDEA查看Git历史记录,复制当前版本号. 2.记录当前版本号后,再复制你要回退的版本号 ...