环形链表入口节点

Posted icecrea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了环形链表入口节点相关的知识,希望对你有一定的参考价值。

环形链表入口节点

题目描述

技术图片

思路分析:判断是否有环

本题是判断环形链表是否有环的进阶题目。

环形链表题目利用了双指针技巧,设置快慢两个指针,每次快指针走两步慢指针走一步。假如链表有环,那么快慢指针在环的部分终究会相遇。

判断链表是否有环,比较简单直接上代码。

public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }

思路分析:如何找出入口节点?

方便分析作图如下,假设a是链表头节点,b是链表中环的入口节点,c是快慢指针相遇的节点。

三段路径长度按顺时针分别称为ab,bc,cb。链表中环长度为s = bc + cb。

技术图片

我们需要计算,在c点相遇的时候,快慢指针分别走了多远?利用快慢指针长度的两倍关系去求解问题。

快指针到c点走过的长度 s1 = ab + bc + n * s = ab + bc + n * (bc + cb)

快指针走的距离包括ab+bc的距离,再加上在圆环中环绕圈数的距离。

那么慢指针的长度如何计算?他在环中也走了多少圈?

这就需要理解一个概念:

从慢指针进入环时,到快慢指针相遇,慢指针走过的距离是一定小于等于环的大小。(如果整个链表是一个环,则慢指针走过距离等于环大小)
慢指针进入环后,可以看成快指针在后面追赶慢指针。

  • 最好情况快指针就在b的左侧,移动一步两者便相遇。
  • 最差情况为快指针在b的右侧,需要多绕不到一圈的距离。

分析最差情况:假设慢指针走了一整圈环,回到b点时快指针才追上,慢指针移动长度为s,快指针移动长度则为2s-1。但实际上快指针速度是慢指针两倍,即实际应走距离为2s。可以反证出在慢指针走完一圈之前,快指针必然追上慢指针。

理解了上述概念,可以很快得出,慢指针长度 s2 = ab + bc

已知了快指针是慢指针两倍,即 2 (ab + bc) = ab + bc + n (bc + cb)

推出:ab = (n-1) * (bc + cb) + cb

含义为:链表头到环入口的距离=相遇点到环入口的距离+(n-1)圈环长度

分析到这里,其实已经得出答案了。两个链表相遇后,一个指针指向相遇的位置,一个指针指到链表的头节点。两个指针均向后移动,判断当两者相同时,该节点便是链表到环的入口节点。

代码参考:

 public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }

        // 步骤一:使用快慢指针判断链表是否有环
        ListNode fast = head;
        ListNode slow = head;
        boolean hasCycle = false;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                hasCycle = true;
                break;
            }
        }

        // 步骤二:若有环,找到入环开始的节点
        if (hasCycle) {
            ListNode cur = head;
            while (slow != cur) {
                cur = cur.next;
                slow = slow.next;
            }
            return slow;
        }

        return null;
    }

公众号:编程呓语,分享原创优质技术文章,欢迎大家关注。

技术图片

以上是关于环形链表入口节点的主要内容,如果未能解决你的问题,请参考以下文章

环形链表找入口节点(Python and C++解法)

寻找环形链表的入口点

142. 环形链表 II-找环入口-快慢指针

剑指Offer:环形链表的入口

剑指Offer:环形链表的入口

图解 LeetCode142:环形链表,你的入口在哪?