> For the complete documentation index, see [llms.txt](https://junnie.gitbook.io/nine-chapter/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://junnie.gitbook.io/nine-chapter/5.linkedlist/summary/pan-duan-lian-biao-shi-fou-bao-han-huan.md).

# 判断链表是否包含环

这个技巧也在前文 [双指针技巧汇总](https://labuladong.github.io/algo/2/21/56/) 写过，如果看过的读者可以跳过。

判断链表是否包含环属于经典问题了，解决方案也是用快慢指针：

每当慢指针 `slow` 前进一步，快指针 `fast` 就前进两步。

如果 `fast` 最终遇到空指针，说明链表中没有环；如果 `fast` 最终和 `slow` 相遇，那肯定是 `fast` 超过了 `slow` 一圈，说明链表中含有环。

只需要把寻找链表中点的代码稍加修改就行了：

```java
boolean hasCycle(ListNode head) {
    // 快慢指针初始化指向 head
    ListNode slow = head, fast = head;
    // 快指针走到末尾时停止
    while (fast != null && fast.next != null) {
        // 慢指针走一步，快指针走两步
        slow = slow.next;
        fast = fast.next.next;
        // 快慢指针相遇，说明含有环
        if (slow == fast) {
            return true;
        }
    }
    // 不包含环
    return false;
}
```

当然，这个问题还有进阶版：如果链表中含有环，如何计算这个环的起点？

这里简单提一下解法：

```java
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;
}
```

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

前文 [双指针技巧汇总](https://labuladong.github.io/algo/2/21/56/) 详细解释了其中的原理，这里简单说一下。

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

[![](https://labuladong.github.io/algo/images/%E5%8F%8C%E6%8C%87%E9%92%88/3.jpeg)](https://labuladong.github.io/algo/images/%E5%8F%8C%E6%8C%87%E9%92%88/3.jpeg)

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

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

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

[![](https://labuladong.github.io/algo/images/%E5%8F%8C%E6%8C%87%E9%92%88/2.jpeg)](https://labuladong.github.io/algo/images/%E5%8F%8C%E6%8C%87%E9%92%88/2.jpeg)

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

### **可用loop解决的问题（注意这里fast,slow都从head开始）**

1.环的长度是多少？

2.如何将有环的链表变成单链表？（解除环）

3.如何判断两个单链表是否有交点？如何找到第一个交点？

![](/files/-M33gu76jPWhoosM17l7)

（Y为环的第一个点，Z是fast,slow第一次相遇的点）

***Solution for 1.环的长度***

1>第一相遇后，让fast,slow继续走，记录下下次相遇时环走了几次。

fast第二次到Z时，fast走了一圈，slow半圈。

fast第三次到Z时，fast走了两圈，slow一圈，正好有在Z相遇。

2>第一次相遇后，fast不走，让slow走，下次相遇时环的长度。

3>**(Easiest)**&#x7B2C;一次相遇时slow走了a+b, fast走了a+b+c, 因为fast速度是slow的两倍，所以2(a+b)=a+b+c+b

所以a=c

环的长度L=a+b=b+c, 到**第一次相遇，slow经过的长度就是环的长度L**.

***Solution for 2: 解除环变成单链表***

将C段中Y之前的点与Y切断即可。

***Solution for 3: 判断两个单链表是否有交点，有的话第一个交点在哪里 (LeetCode 160)***

Version 1:&#x20;

step 1:先判断两个链表是否有环，若一个有环一个没环 ->不相交

step 2:都没环，判断其尾部是不是相等

step 3:都有环，判断一个链表上的Z点是否在另一个链表上。

**Version 2（recommend）**:

让L1 tail.next= L2.head. 判断是否有环，有环就有交点。

**找到第一个相交的点** (LeetCode 160)：

**求出 L1 L2的长度**，若L1\<L2,用fast slow分别从头部开始走，L2先走（L2-L1）步，然后在一起走，直到相遇。

***Solution for LinkedList Cycle II (LeetCode 142)--找到Y点***

因为a=c,第一次相遇在Z之后让两个指针fast留在Z， slow变回head开始走，每次走一步，正好在Y相遇。
