142.Linked List Cycle II (M)

https://leetcode.com/problems/linked-list-cycle-ii/

1.Description(Hard)

Given a linked list, return the node where the cycle begins.

If there is no cycle, returnnull.

Example

Given-21->10->4->5, tail connects to node index 1,return10

2.Code

这道题目是要求求出环的入口处,没有环的话就输出null

因为a=c (详见summary) 所以一个点从head走,一个从第一次相遇的点走,每次都走一步,再次相遇的点就是环的入口处

    public ListNode detectCycle(ListNode head) {  

        ListNode slow = head;
        ListNode fast = head;

        while (true) {
            if (fast == null || fast.next == null) {
                return null;    //遇到null了,说明不存在环
            }
            slow = slow.next;
            fast = fast.next.next;
            if (fast == slow) {
                break;    //第一次相遇在Z点
            }
        }

        slow = head;    //slow从头开始走,
        while (slow != fast) {    //二者相遇在Y点,则退出
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

这里简单提一下解法:

ListNode detectCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) break;
    }
    // 上面的代码类似 hasCycle 函数
    if (fast == null || fast.next == null) {
        // fast 遇到空指针说明没有环
        return null;
    }

    // 重新指向头结点
    slow = head;
    // 快慢指针同步前进,相交点就是环起点
    while (slow != fast) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

可以看到,当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。

前文 双指针技巧汇总 详细解释了其中的原理,这里简单说一下。

我们假设快慢指针相遇时,慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步:

fast 一定比 slow 多走了 k 步,这多走的 k 步其实就是 fast 指针在环里转圈圈,所以 k 的值就是环长度的「整数倍」。

假设相遇点距环的起点的距离为 m,那么结合上图的 slow 指针,环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。

巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。因为结合上图的 fast 指针,从相遇点开始走k步可以转回到相遇点,那走 k - m 步肯定就走到环起点了:

所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后一定会相遇,相遇之处就是环的起点了。

Last updated