原题出处:https://leetcode.cn/problems/c32eOV/description/
PS:题目描述:
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null 。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1 ,则在该链表中没有环。注意, pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。

题目描述过于抽象,简单一点就是找出该链表的入环点。
说这道题目之前,想先聊一聊小学问题追击问题,假设有两个人A、B在一个环形跑道上跑步,他们的跑步速度每时每刻都是不受外部影响的,A的速度是3m/s,B的速度是4m/s,试问:他们会在什么时候相遇?这是小学的奥数题,也称为追击问题。
这道题的思路和追击问题一个思路,跟找一个链表的中点思路一致,使用两个快慢指针,快指针一次走2步,慢指针一次走1步,当fast指针进入环的起点的时候,假设此刻fast指针走了L个单位距离,那么此时该点距离slow指针就还有L/2个单位距离,然后让slow再继续走L/2个单位距离,此刻,fast与slow就都在环内部了,而此刻就由fast来追slow,设他们的距离为N个单位距离,因为他们没走一次,距离差值就会减1,那么最终肯定会相遇。这道题有2个隐藏坑点,一个是fast在环内是否是只走了一圈,第二个是为什么要用快慢指针,一个走一步,一个走两步,现在 先卖个关子提出问题,让大家思考一下,在最后我会跟大家娓娓道来。
上步骤图:





设:进入环点之前的距离为L,slow走的距离为X,环的距离为C
可看出slow走的距离为L+X,然而此刻,fast走了 多少距离呢?,是slow的两倍,即2(L+X)
得出公式: .slow* 2 = fast
再想一下,fast实际是走了哪些节点呢?首=首先有这段距离,然后进入环内,那么环走了几圈呢?假设这个环的长度非常大,多大呢?足足可以容纳L/2个单位距离,为什么是L/2呢?假设不能容纳该大小单位,那么在slow进入环内,不是fast柚走了一圈C ,也就是说,在环内,fast将会出现一些特殊情况, 例如走了多圈
那么现在可得出以下公式: .
2*(L + X)=L+ NC+ X
L+X=NC
L= NC- X
L= (N- 1)*C+ N- X
最后得出两个新链表的长度:
L + X (指向head)
N - X(指向相遇节点)
然后按照这个思路来写代码
struct ListNode *detectCycle(struct ListNode *head) {
if(!head)//如果给的空链表,就直接返回
return head;
struct ListNode *slow = head, *fast = head;//定义两个快慢指针,这种方法也可以适用于找链表中点
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;//让fast走的步数,始终为slow的两倍
if(slow == fast)//当他们相同了,就代表此刻相遇了,此时他们的距离为N
{//然后就开始找相遇点
struct ListNode* MeetNode = slow;
struct ListNode* NewHead = head;
while(MeetNode != NewHead)开始遍历,当相等了,那么就找到入环起始点了
{
MeetNode = MeetNode->next;
NewHead = NewHead->next;
}
return MeetNode;
}
}
return NULL;
}