链表 | 链表中环的入口结点

题目描述

给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null.

如下图所示, 两段链表{ 1 , 2 } , { 3 , 4 , 5 } , 头结点是1, 环节点是3,{ 3 , 4 , 5 } 成环.
我们的目标就是在给定的一个链表中找出环节点, 即节点3.

题目考察内容

  1. 链表的快慢指针:了解快慢指针的基本思想。
  2. 判断链表是否有环:实现一个函数判断链表是否有环。
  3. 找到环的入口节点:如果链表有环,实现一个函数找到环的入口节点。

解题思路

什么是链表的快慢指针?

快慢指针判断链表是否有环是一种非常巧妙的思想,其基本思路是使用两个指针,一个慢指针每次前进一步,一个快指针每次前进两步。如果链表中存在环,那么快指针最终会追上慢指针;如果没有环,快指针会先到达链表尾部。

基于上述快慢指针思想, 如何判断链表是否有环?

  1. 定义慢指针和快指针:将慢指针 slow 指向链表头部,将快指针 fast 指向链表头部的下一个节点。为了避免一开始就相遇,快指针 fast 比慢指针 slow 先前进一步。

  2. 判断条件:在循环中,我们首先判断 slowfast 是否相等,如果相等,说明快慢指针相遇,链表中有环,返回 true

  3. 快慢指针移动:在循环中,每次慢指针 slow 前进一步,快指针 fast 前进两步。这样,如果链表中存在环,快指针会一直在环内转动,最终追上慢指针;如果链表中不存在环,快指针会率先到达链表尾部。

  4. 循环条件:循环条件是 slow != fast,如果快慢指针相遇了,循环结束,函数返回 true;如果快指针到达链表尾部,即 fast == null || fast.next == null,说明链表中没有环,循环结束,函数返回 false

 public boolean hasCycle(ListNode head) {

        //对头结点就行校验
        if (head == null || head.next == null) {
            return false;
        }

        ListNode slow = head;      // 慢指针
        ListNode fast = head.next; // 快指针

        // 判断链表是否有环
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false; // 如果快指针到达链表尾部,说明没有环
            }

            slow = slow.next;         // 慢指针前进一步
            fast = fast.next.next;    // 快指针前进两步
        }

        return true; // 快慢指针相遇,说明链表有环
    }

如何找到环的入口结点

根据上述图示,A代表头结点,B代表环节点,C代表相遇点C, 环中一圈长度我们用L标识.

基于判断是否链表是否有环的思想, 这一次我们让快慢指针同时从头结点A出发,每一次慢指针移动一步,快指针移动2步,最终第一次相遇在相遇点C.

在相遇点C, 慢指针与快指针相遇, 相遇的时候, 可能快指针比慢指针跑了n圈环中圈数。

- 慢指针走过的距离: x + y

- 快指针走过的距离: x + y + n * L

根据快慢指针距离的关系, 基于“每一次慢指针移动一步,快指针移动2步”, 快指针走过的距离一定慢指针的两倍。

距离推导公式:

 x + y + n* L = 2( x + y)   --> x = (n−1)⋅L+(L−Y)

这个是什么意思呢?将上述推导出来的转变成距离描述如下:

链表头到环入口的距离 =(n-1)* 圈数环长度 + 相遇点到环入口的距离(B点到C点)

链表头到环入口的距离可以理解成从头结点开始遍历, 走过链表头到环入口的距离这个距离又等于(n-1)* 圈数环长度 + 相遇点到环入口的距离。

(n-1)* 圈数环长度 + 相遇点到环入口的距离可以这样理解, 当快慢指针在相遇点C相遇后,如果从快慢指针相遇的节点的位置继续前进, (n-1)* 圈数环长度 表示是完整的一圈, 意味着从相遇点经过多少圈仍然在C的位置,还得加上相遇点到环入口的距离也就是图中相遇点C-环节点B的距离.

结论: 因此到了这里,解答这个题目的思路就明显了,先是定义了快慢指针, 如果快慢指针相遇,表明这个链表有环, 在有环的前提下,从快慢指针相遇的C点继续前进,继续再定义一个指针从头节点A出发,当头节点A走过链表头到环入口的距离, 根据等式表明相遇C点继续前进走过(n-1)* 圈数环长度 + 相遇点到环入口的距离,是不是会相遇?

相遇的这个点就是我们要求的环节点入口!!!

解题java代码

 public ListNode EntryNodeOfLoop(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;

        // 判断链表是否有环
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

            if (slow == fast) {
                // 相遇,存在环
                ListNode entry = head;

                // 找到环的入口节点
                while (entry != slow) {
                    entry = entry.next;
                    slow = slow.next;
                }

                return entry;
            }
        }

        return null; // 无环

    }

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值