题目描述:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改链表。
示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
思路:
双指针会产生两次“相遇”
双指针的第一次相遇:
设两指针 fast,slow 指向链表头部 head 。
令 fast 每轮走 2 步,slow 每轮走 1步。
执行以上两步后,可能出现两种结果:
第一种结果: fast 指针走过链表末端,说明链表无环,此时直接返回 null。
如果链表存在环,则双指针一定会相遇。因为每走 1 轮,fast 与 slow 的间距+1,fast 一定会追上 slow 。
第二种结果: 当fast == slow时, 两指针在环中第一次相遇。下面分析此时 fast 与 slow 走过的步数关系:
设链表共有 a+b 个节点,其中 链表头部到链表入口 有 a 个节点(不计链表入口节点), 链表环 有 b 个节点(这里需要注意,a和 b 是未知数,例如图解上链表 a=4, b=5);设两指针分别走了 f,s步,则有:
fast 走的步数是 slow 步数的 2 倍,即 f=2s;( fast 每轮走 2步)
fast 比 slow 多走了 n 个环的长度,即 f=s+nb;( 双指针都走过 a 步,然后在环内绕圈直到重合,重合时 fast 比 slow 多走 环的长度整数倍 )。
将以上两式相减得到 f=2nb,s=nb,即 fast 和 slow 指针分别走了 2n,n 个环的周长。
双指针第二次相遇:
令 fast 重新指向链表头部节点。此时 f=0,s=nb。
slow 和 fast 同时每轮向前走 1 步。
当 fast 指针走到 f=a步时,slow 指针走到 s=a+nb 步。此时两指针重合,并同时指向链表环入口,返回 slow 指向的节点即可。
python:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast,slow=head,head
while True:
if not (fast and fast.next):
return
fast,slow=fast.next.next,slow.next
if fast==slow:
break
fast=head
while fast!=slow:
fast,slow=fast.next,slow.next
return fast