环形链表I、II(含代码以及证明)
环形链表
解题思路
- 定义两个指针,一个快指针,一个慢指针,快指针每次移动两个节点,慢指针每次移动一个节点。
- 从头节点开始,让快慢指针同时移动,如果链表中有环,那么快慢指针一定会在某个节点相遇。
- 如果快慢指针相遇了,说明链表中有环,返回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
解题思路
- 定义两个指针,一个快指针fast,一个慢指针slow,都从头节点head开始遍历链表。
- 快指针每次走两步,慢指针每次走一步,如果链表有环,那么快慢指针一定会在环内相遇,否则快指针会先遍历完链表(遇到null)。
- 如果快慢指针相遇,说明链表有环,此时将快指针重新指向头节点head,慢指针保持在相遇节点,然后快慢指针都每次走一步,直到再次相遇,相遇的节点就是入环的第一个节点。即:从头节点到入环节点的距离,等于从相遇节点继续走到入环节点的距离。
- 如果快慢指针没有相遇,说明链表无环,返回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(含代码以及证明)的更多相关文章
- 代码随想录第四天| 24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、160.链表相交、142.环形链表II
		今天链表致死量 第一题 public static class ListNode { int val; ListNode next; ListNode() {} ListNode(int val) { ... 
- LeetCode142 环形链表 II
		给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 说明:不允许修改给定的链表. 进阶:你是否可以不用额外空间解决此题? //章节 - 链表 //二.双指针技巧 //2.环 ... 
- 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II
		[算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ... 
- Leetcode 142.环形链表II
		环形链表II 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 说明:不允许修改给定的链表. 进阶:你是否可以不用额外空间解决此题? 链表头是X,环的第一个节点是Y,sl ... 
- LeetCode 142:环形链表 II Linked List Cycle II
		给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 - ... 
- Leetcode.142-Linked-list-cycle-ii(环形链表II)
		环形链表II 思路 https://www.cnblogs.com/springfor/p/3862125.html https://blog.csdn.net/u010292561/article/ ... 
- LeetCode 142. 环形链表 II(Linked List Cycle II)
		142. 环形链表 II 142. Linked List Cycle II 题目描述 给定一个链表,返回链表开始入环的第一个节点.如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整 ... 
- Java实现 LeetCode 142 环形链表 II(二)
		142. 环形链表 II 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 ... 
- [LeetCode题解]142. 环形链表 II | 快慢指针
		解题思路 本题是在141. 环形链表基础上的拓展,如果存在环,要找出环的入口. 如何判断是否存在环,我们知道通过快慢指针,如果相遇就表示有环.那么如何找到入口呢? 如下图所示的链表: 当 fast 与 ... 
- 【LeetCode】142. 环形链表 II
		142. 环形链表 II 知识点:链表:set:快慢指针 题目描述 给定一个链表,判断链表中是否有环. 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表 ... 
随机推荐
- Vue3 企业级优雅实战 - 组件库框架 - 4 组件库的 CSS 架构
			在前一篇文章中分享了搭建组件库的基本开发环境.创建了 foo 组件模块和组件库入口模块,本文分享组件库的样式架构设计. 1 常见的 CSS 架构模式 常见的 CSS 架构模式有很多:OOCSS.ACS ... 
- MYSQL5.7 保姆级安装教程
			现在要是说mysql是什么东西,就不礼貌了 虽然有的同学没有进行系统的深入学习,但应该也有个基本概念 [不了解也没关系,后续会进行mysql专栏讲解]简单来说,存储数据的 学习mysql,就要先安装它 ... 
- 基于Spring-AOP的自定义分片工具
			作者:陈昌浩 1 背景 随着数据量的增长,发现系统在与其他系统交互时,批量接口会出现超时现象,发现原批量接口在实现时,没有做分片处理,当数据过大时或超过其他系统阈值时,就会出现错误.由于与其他系统交互 ... 
- pagehelper踩坑记之分页乱套
			我们在使用数据库进行查询时,很多时候会用到分页展示功能,因此除了像mybatis这样的完善的orm框架之外,还有pagehelper这样的插件帮助减轻我们的工作. pagehelper的实现方式是,不 ... 
- python调用c++生成的dll
			前言 这个我查询了很多资料,所以到此为止,相当于做一个总结 c++代码如何生成dll #include<iostream> using namespace std; extern &quo ... 
- 如何发布一个 TypeScript 编写的 npm 包
			前言 在这篇文章中,我们将使用TypeScript和Jest从头开始构建和发布一个NPM包. 我们将初始化一个项目,设置TypeScript,用Jest编写测试,并将其发布到NPM. 项目 我们的库称 ... 
- Java-递归查询法
			递归查询用户所在团队的老大的用户id(一个团队中,只有一个老大,也就是父级id="-1") 如下:是表结构 first_agent_id----用户的上级id user_id--- ... 
- 使用WPF或AspNetCore创建简易版ChatGPT客户端,让ChatGPT成为你的私人助理
			前言:前一天写的一个ChatGPT服务端,貌似大家用起来还不是那么方便,所以我顺便用WPF和AspNetCore的webapi程序做个客户端吧,通过客户端来快速访问chatgpt模型生成对话. 1 ... 
- 前端h5适配刘海屏和滴水屏
			前端适配苹果刘海屏,安卓刘海屏水滴瓶 其实w3c早就为我们提供了解决方法(CSS3新特性viewport-fit) 在w3c.org官方给出的关于圆形展示(Round display)的标准中, 提到 ... 
- [Linux Kernel 源码分析] 通过vconfig配置vlan的系统调用/驱动流程分析
			By YuCloud (蓝天上的云℡ - 博客园 https://www.cnblogs.com/yucloud/) 转载请注明出处 vconfig源码分析 vlan/vconfig.c at mas ... 
