带环问题,图文分析链表环节点相关问题。

求链表的环节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RxodeNrU-1638789923261)(C:\Users\Z-zp\AppData\Roaming\Typora\typora-user-images\image-20211204141258124.png)]s


这道题的思路应该是这样的:

  • 先判断有没有环。

1.判断有没有环

我们通过 快慢指针 的方法,可以确认是否有环的存在,直接上图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7I9CBQU-1638789923262)(C:\Users\Z-zp\AppData\Roaming\Typora\typora-user-images\image-20211204143031071.png)]

先初始化,让fast和slow都指向头结点(起点),slow每次走一步,fast每次走两步。

while(fast&&fast->next)
{
   slow=slow->next;
   fast=fast->next->next;
   if(fast==slow)
   {
   	return true;    
   }
}

	return false;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQNiBbmK-1638789923262)(C:\Users\Z-zp\AppData\Roaming\Typora\typora-user-images\image-20211204143553197.png)]

while 这么写的依据是,如果无环,则slow在走完前不可能和fast相遇,并且在fast走完后,循环就停止了,返回false

如果有环,当fast在环里饶了n环后,终于等到了slow。再回顾一下,通过上述,我们知道证明有环的条件就是fast与slow相遇。

那我们又是怎么知道这样走fast一定会和slow相遇,而不会谁多走一步或者少走一步刚好错过呢,会不会永远不会相遇?

这就又要另外细说了😸


###为什么fast和slow一定会相遇

在这里插入图片描述

当slow进入环的那一刻,fast与slow之间的最大距离就已经确立,fast开始追slow,设此时fast与slow相距路程N

fast每走两步,slow走一步,则相当于slow不动,fast走一步,路程=N-1

fast 2步 slow 1步 …

N-1

N-2

N-3

.

.

.

0

到最后,fast一定会与slow相遇。以上证明了 fast走两步,slow走一步,如果存在环,它们最后一定会相遇。

代码

struct ListNode 
{
     int val;
     struct ListNode *next;
};
 
bool hasCycle(struct ListNode *head) {
    struct ListNode*slow,*fast;
    slow=fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)
        {
        return true;
        }
    }
    return false;
}

拓展: 那如果fast走3步、走4步、…走n步呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZnYEQocz-1638789923264)(C:\Users\Z-zp\AppData\Roaming\Typora\typora-user-images\image-20211205080430158.png)]

假设刚入环时,slow和fast相距路程N为偶数。

fast每走三步==(奇数步)==,slow走一步,则相距路程N的变化为

N-2

N-4

N-6

.

.

.

0

N的变化图

在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/9b482f0bac0a4e368a0305a59376a073.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFpTGFuXw==,size_20,color_FFFFFF,t_70,g_se,x_16

我们设 fast在后面追slow的方向为正,则-1的含义是fast在slow前距离为1的地方。

fast和slow的距离为N此时变成了C-1。如果fast想追上slow 他需要再跑一趟,

注意:

有些小伙伴会有疑问,fast是已经经过了slow,和slow相遇后,然后才到slow前面的,难道这不算相遇吗?

注意:这道题的本质还是链表,判断相遇点的代码是这样的

while(fast!=slow)
{
    slow=slow->next;
    fast=fast->next->next;
}

所以 fast与slow相遇后才走到slow前的说法 其实是不正确的

从代码中我们可以看出,fast最终被赋值为 fast的next的next。 也就是说fast是跳到了slow的前面,并没有在slow处的停留,也就不存在相遇。

举个例子:你对别人讲你开车经过了北京,你能说你到过北京吗。不能吧,这是有区别的。

回到正题

回顾一下 ,上面我们假设过 :slow进环时,fast、slow相距路程为N,环的长度为C。

N变成了C-1,即N=C-1;

上述说过,fast走奇数步,且N为奇数时,最后路程一直减减减,会减到-1,(忘了的小伙伴点目录里的N的变化图再上去看一眼

而减到-1就意味着错过了,要再重新追slow

因此,当C为偶数时,即C-1为奇数,经过第一轮追赶后,N=C-1为奇数,则第二轮追赶又错过了,第三轮追赶,又变成了N=C-1。…

因此,针对 如果fast走3步、走4步、…走n步是否会相遇的问题,我们有如下:

结论

无论fast走多少步,这都不重要,重要的是如果slow进环时,fast与slow相距路程N为奇数,且环的长度C为偶数(C-1为奇数),那么fast与slow将会在环里一直打圈,永远无法相遇

2.环节点的位置分析

前方高能!!!
在这里插入图片描述

还是这个图,现在我们开始推结论。

slow、fast相遇时(注意:这里是slow每走一步,fast走两步)

slow所走路程为fast所走路程为
L+xL+n*c+x

解释一下n*c,因为环的长度未知,如果环很大,可能slow入环前fast还没走完一圈,也可能环很小,slow进环前fast已经走了很多圈。

已知fast的步数是slow的两倍,则fast所走路程为slow的两倍,有:

2(L+x)=L+n*c+x,则L=n*c-x

直线的尽头是环口。

在这里插入图片描述

既然L=n*c-x,那我们就把x减掉,从减掉x后的起点出发,即相遇点,那这段减掉了x的路程,就和L一样长。

意思是,只要fast从 相遇点 出发(转几圈其实不重要),slow从 起点 出发,当slow走到入环口的时候,fast也走到入环口了,他们相遇的位置,就是入环口,

而在第一部分的时候,已经提到了如何判断是否存在环——>slow、fast最后会相遇。

那时候就已经可以得到了他们的相遇点。

因此,求环的入口,只要将fast和slow分别赋值为:

fast=meet(相遇点)
slow=head(起点)

然后一直循环直到他们相遇,记录这个点,即是入环节点。

代码

struct ListNode* detectCycle(struct ListNode* head)
{
    struct ListNode* slow, * fast;
    slow = fast = head;
    struct ListNode* meet;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            struct ListNode* meet = fast;
            slow = head;
            while (meet != slow)
            {
                slow = slow->next;
                meet = meet->next;  //因为路程一样长,所以meet和slowd都是一次走一步。
            }

            return meet;
        }
    }

    return NULL;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿波呲der

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值