环形链表

解题思路

  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. Go语言书籍推荐

    附录: 其他书籍的介绍:https://github.com/golang/go/wiki/Books Go程序设计语言 (圣经) 作者 艾伦 A.A.多诺万 Kernighan 下载地址 中文版PD ...

  2. JS图片放大镜功能实现

    JS图片放大镜功能实现 技术关键点 1.左侧和上侧距离,在一个水平位置和垂直位置中有我们可以挪动的区域,就是原图片区域,鼠标挪动位置是一个块状位置,他的左侧和上侧距离浏览器上侧和左侧分别有一个长度,我 ...

  3. 8 STL-stack

    ​ 重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦!  生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要介绍S ...

  4. python关于error: invalid command 'bdist_wheel报错的解决

    看了很多解决办法,大部分在扯去下载一个 .whl 源文件然后在pip 安装,经过我亲自测试执行完这句即可解决! pip3 install wheel

  5. Halo 主题 Redemption 首发版

    Redemption 一款专注阅读.写作的 Halo 博客主题.主要设计思想即是专注阅读.写作,是一款极简类型的博客主题. Redemption 部分设计灵感借鉴 Halo 博客 Zozo 主题,感谢 ...

  6. VulnHub靶机渗透实战9-vikings

    ​本次靶机是CTF风格的靶机. 靶场地址:Vikings: 1 ~ VulnHub 网络呢还是桥接模式. Description Back to the Top A CTF machine with ...

  7. 更改HTML请求方式的几种方法

    以ctfhub中的请求方式题目为例,则可以有: 法一:通过burpsuite抓包修改 在burpsuite中抓包后发送到repeater模块中,对请求方式进行修改即可 法二:通过curl命令进行 cu ...

  8. MySQL进阶实战6,缓存表、视图、计数器表

    一.缓存表和汇总表 有时提升性能最好的方法是在同一张表中保存衍生的冗余数据,有时候还需要创建一张完全独立的汇总表或缓存表. 缓存表用来存储那些获取很简单,但速度较慢的数据: 汇总表用来保存使用grou ...

  9. 【Java SE进阶】Day13 Stream流、方法引用

    〇.总结 Stream流的方法:forEach.filter.map.count.limit.skip.concat(结合之前的Collectors接口) 方法引用:Lambda的其他类方法体相同,如 ...

  10. python什么是异常?如何处理异常

    异常处理 什么是异常 异常是程序错误发生的信号.程序一旦出现错误,就会产生一个异常,如果程序中没有处理该异常,该异常就会抛出来,程序的运行也随即终止. 错误分为两种 1.语法错误 2.逻辑错误 如何处 ...