问题1:为什么带环时fast2步slow1步会相遇
先写出证明带环代码、

使用快慢指针证明相遇
bool hasCycle(struct ListNode *head) {
struct ListNode *fast=head;
struct ListNode *slow=head;
//快慢指针
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
return true;
}
return false;
}
这里的代码虽然是正确的,但是我们要探究为什么快慢指针fast与slow一定会相遇,如果fast一次走3步呢?
问题一.1:fast为什么会与slow在环内相遇
这里我们先确定,链表一定带环,才可以以下推导。
fast每次走两步
fast=fast->next->next;
slow每次走一步
slow=slow->next;
使用fast一定先入环,在fast入环时,slow里入环还要走之前的一倍距离

这里我们设L为入环前的长度,当fast入环的的那一刻,slow走了L/2步。
当slow入环时有两种情况,刚好与fast相遇,与和与fast顺时针差N步,这里我们定义fast顺时针到slow的距离为N

fast与slow在入口点相遇

差N的步数
假设避开刚刚好在入口处相遇,所以fast理slow有N步。
slow和fast继续以自己的速度走,直到相遇,相遇点距离洞口以洞口为出发点顺时针距离定义为X,

这里我们要探究为什么会相遇。
首先,我们知道fast每次是2步,slow每次是一步
所以,他们的相对数是1步,证明当slow入环时,fast开始以一步一步的距离接近slow,
刚开始距离时N然后N-1,N-2,N-3.....2,1,0。当距离为零时,fast与slow相遇,
问题一.2:如果fast走三步,或者走四..N步呢?
根据上面我们知道了其实就是根据相对速度才能知道是否会相遇。
如果fast走三步
相对速度为2,分情况讨论
情况一:当N(fast,slow差)为偶数时,距离变化为N,N-2,N-4,N-6......4,2,0.当距离为零时,fast与slow相遇。
情况二:当N为奇数时,N-2,N-4,N-6......5,3,1,-1
这里出现了一次fast越过了slow,没有相遇,这时候要看下一次的初始值
这里我们设环一圈为C,
所以第二次差了C-1的距离,这时候我们也要在情况二中分类讨论
1:C-1也是奇数的话,这时候就是重复了情况二,所以fast和slow永远不会相遇。
2:C-1是偶数的话,这时候就是重复了情况一,所以fast和slow会相遇。
如果fast走N步
根据fast走3步slow走1步得出结论如果不是以一为相对速度时,相遇条件中相对速度和距离有关系,以及C-1的长度。
所以如果不是一(fast-slow>1)的速度追赶。那么就要计算IF(N%(fast-slow)||(C-1)%(fast-slow)||(C-2)%(fast-slow)||......),这里的或上线为,fast-slow-1的值。
问题二:当链表带环,如何寻找节点并证明结论
力扣题目:142. 环形链表 II - 力扣(Leetcode)
先写出代码:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *slow;
struct ListNode *fast;
slow=fast=head;
while(fast&&fast->next)
{
//利用快慢指针写出当fast入环时slow为才走入环前的一半。
fast=fast->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode*meet=fast;
struct ListNode*start=head;
while(meet!=start)
{
meet=meet->next;
start=start->next;
}
return meet;
}
}
return NULL;
}

这里我们先要给个必要条件:链表一定带环
slow与fast一定相遇。

设:L为入环前的长度,X为入口点顺时针到相遇点的距离,C为环的长度
推导fast第一个路程公式:2*(L+C)
这里我们依旧是fast一次走2步,slow一次走1步。
所以fast相对速度为slow的2倍。
这里我们先要证明slow有没有可能绕fast一圈,答案是不可能的。
首先我们设想fast与slow开始追击的最长距离C-1,
fast以相对速度1步追击slow

这个时候,slow刚刚入环而fast刚刚好越过了入口点一步;
所以fast一步一步追击slow需要追击N=C-1次才可以追上。
由于fast一次2步,所以fast追上slow需要跑的距离是2*(C-1);
由于fast为slow的二倍速度,
所以slow也最多跑C-1距离;
所以无论如何这样的情况slow不可能跑一圈的;

X为slow入环后到和fast相遇的距离
所以:X大小绝对小于C大小:X<C;

这里我们就可以知道slow从头走到尾巴的距离为slow=L+X;
而fast为slow的二倍速度,所以fast与slow相遇时候走的距离为slow距离的二倍
所以fast走的路程为:2*slow=2*(L+X);
继续推导fast另一个路程公式:(K-1)*C+C+X+L
判断fast在环内转了K*C圈(有的地方认为slow入环前fast只有多走了一圈:但是2有种情况就是L远大于C和C远大于L)

情况1:L=100,C=2,

情况2:L=2,C=100
所以说明slow进圈前,fast已经在圈里转了K圈(未知数),路程为K*C;
入环点距离相遇点距离为X步
而fast追击slow可能至少要转一圈,然后再走X步和slow相遇,哪怕在入环点直接相遇fast都要走C步,只是这个时候X=0;

Z:slow入环时,fast当前圈走的路程,M:slow入环时,当前圈还未走的距离。
Z : slow入环时,fast当前圈走的路程。
M:slow入环时,fast当前圈还未走的距离。
当slow入环时,此时fast开始追击。

fast走完当前圈,继续追击slow
X0为fast到入环点时,slow在环内已经走的距离,slow再走X1到相遇点
所以fast再走X1+X2步就是相遇点了,因为X1+X2==slow在圈内行走的距离X:
所以fast在圈内要走C+X才可以到达相遇点,
又因为特殊情况1:所以假设slow入环前fast已经走了K*C(K>0);
已知fast入环前走了L步;
综上所述:fast的另一个公式为K*C+X+L;
合并fast俩个表达式
fast=2*(L+X);
fast=K*C+X+L;
2*(L+X)=K*C+X+L
L+X=K*C
L=K*C-X;
K可为任何大于0的整数;
假设K==1得L=C-X;
看图

C-X得距离刚刚好是相遇点到入环点
所以相遇点顺时针到入环点得距离刚刚到等于头节点到入环点得距离:C-X==L;
所以从头节点和相遇点,同时一步一步走,当两个指针相等时就是入环得第一个节点:
if(slow==fast)
{
struct ListNode*meet=fast;
struct ListNode*start=head;
while(meet!=start)
{
meet=meet->next;
start=start->next;
}
return meet;
}
谢谢观看。