【数据结构初阶】环形链表

一、判断链表中是否存在环

题目链接:
判断链表中是否存在环

思路

把它转换为追击问题(快慢指针)
快指针比慢指针多走一步,当快指针正好进入环时,慢指针刚好走到一半;
当慢指针正好入环时,快指针在环里的位置是不确定的,可能已经绕环好几圈了,也可能一圈也没绕完,这个取决于环的长度。
快慢指针都进环后,剩下的问题就是追击问题了,当快指针追上慢指针,说明链表有环。
在这里插入图片描述

代码思路:
1.快指针比慢指针多走一步;
2.情况一:如果fast走到空,说明链表是没环的; 情况二:进环-》追击-》追上-》证明链表有环;

bool hasCycle(struct ListNode *head) 
{
    struct ListNode *slow=head;
    struct ListNode *fast=head;

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

    return false;
}

在这里插入图片描述

分析

问题1:为什么slow走一步,fast走两步,他们会相遇?会不会错过?请证明:

在这里插入图片描述

假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走2步
他们之间的距离每次缩小1:即N,N-1,N-2…3,2,1,0,当距离为0时就追上了;

问题2:如果slow走1步,fast走3步,他们会不会相遇?请证明:

假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走3步,他们之间的距离每次缩小2:
情况一:(当N为偶数)N,N-2,N-4…4,2,0—能追上
情况二:(当N为奇数)N,N-2,N-4…5,3,1,-1—错过,设环的周长为C,此时slow和fast的距离变为C-1,
在这里插入图片描述

进入新的一轮追击:
情况一:(当C为偶数,则C-1为奇数)C-1,C-3,C-5…5,3,1,-1—错过,此时fast和slow不可能再相遇了
情况二:(当C为奇数,则C-1为偶数)C-1,C-3,C-5…4,2,0—能追上

总结:
如果slow走1步,fast走X(X>2)步,他们会不会相遇?请证明: 主要看fast和slow他们之间的每走一步相差的距离,如果相差的距离为1,则一定能追上;如果相差的距离为大于1,要分情况讨论,看他们之间的距离是奇数还是偶数;

二、求环的长度

思路:让slow和fast相遇->slow再走一圈(同时记个数)

三、求入环点

题目链接:
求入环点

方法一:(利用fast走的路程永远是slow的两倍建立等式
假设:
起始点~入口点距离为L;
入口点~相遇点距离为X(0<=X<C);(slow在相遇之前不可能走完一圈,因为fast比slow快,所以X<C;也有可能slow和fast在入口点相遇,所以X也可以是0)
环的周长:C;

在这里插入图片描述
slow走过的距离为L+X;
fast走过的距离为L+nC+X;

成立等式:fast走过的距离是slow的两倍=》L+nC+X=2(L+X)=》整理得:L=nC-X
结论:一个指针从起始点走,一个指针从入口点走,会在入口点相遇

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    struct ListNode *meet=NULL;
    struct ListNode *rhead=head;
    
    //找相遇点
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            //一个指针从起始点走,一个指针从相遇点走
            meet=fast;
            while(meet!=rhead)
            {
                meet=meet->next;
                rhead=rhead->next;
            }
            return meet;

        }
    }

    return NULL;
}

方法二:(找入口点,转换成找链表交点(链表相交问题)

相遇点跟相遇点的下一个节点直接断开;
一个指针从起始点开始走算长度,一个指针从相遇点的下一个节点开始走算长度,然后再让长的先走,相交的那个点就是入口点

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int Compare(struct ListNode *circle,struct ListNode *rhead)
{
    int d1=0;
    int d2=0;

    while(circle)
    {
        circle=circle->next;
        d1++;
    }

    while(rhead)
    {
        rhead=rhead->next;
        d2++;
    }

    return d1-d2;

}



struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    struct ListNode *meet=NULL;

    
    //找相遇点
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            //断开结点
            slow=slow->next;
            fast->next=NULL;

            //判断哪一段长
            struct ListNode *rhead=head;
            struct ListNode *circle=slow;
            int ret=Compare(circle,rhead);

            fast=circle;
            slow=rhead;
            if(ret<0)
            {
                fast=rhead;
                slow=circle;
            }
            
            //长的先走
            int distance=abs(ret);
            while(distance)
            {
                fast=fast->next;
                distance--;
            }

            //一起走
            while(fast!=slow)
            {
                fast=fast->next;
                slow=slow->next;
            }

            return fast;

        }
    }

    return NULL;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吃不胖的熊猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值