链表带环问题与第一个环节点

一、如何判断单链表带环

方法:利用快慢指针

设置两个指针,一个名为fast,一个名为slow。两个指针开始指向链表头结点head。

fast一次走两步,slow一次走一步。

当fast进入环后,由于带环链表的next不指向空节点,fast指针一直在环内循环。

当slow也进入环后,fast指针和slow指针也在环内循环,当fast指针和slow指针在环内能够相遇时,表明该链表带环。

判断链表是否带环的函数代码如下:

bool hasCycle(struct ListNode *head) 
{
    struct ListNode*fast=head;
    struct ListNode*slow=head;
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;

        if(slow==fast)
        return true;
    }
    return false;
}

拓展思考

(1)slow一次走一步,fast一次走两步,fast指针会不会和slow指针一直错过,永远追不上呢?

(2)fast指针一次走三步呢?

一、

假设slow指针和fast指针一开始的距离为N,每次fast和slow指针走完后,fast指针和slow指针之间的距离减少1,即为N-1......到最后,fast指针和slow指针距离一定变为0,也就是说,只要slow一次走一步,fast一次走两步,到最后在环内必定能追上。

二、由一可知,如果fast指针一次走三步,那么fast指针和slow指针之间的距离每次减二,于是分为两种情况。

(1)N为偶数

那么到最后fast指针和slow指针必定能相遇

(2)N为奇数

那么fast指针在第一圈会超过slow指针一个节点,设整个环的长度为C,则两个指针此时的距离为C-1,如果C-1为偶数,则可以追上;否则C-1为奇数,两指针追不上。

但是,C为偶数,N为奇数导致两个指针永远错过这种情况真的存在吗?

假设带环链表之前的长度为L,fast在slow进环前已经转了x圈,此时两指针距离为N

那么slow走的路程为:L

fast走的路程为:L+x*C+N

因为此时fast指针走的路程是slow的三倍:L+x*C+N=3L

                                                                   2L=x*C+N

此时N为奇数,C为偶数,2L必定为偶数,那么x*C必定为偶数,N为奇数,偶数加奇数必定为奇数,与2L必定为偶数不符,所以这种情况不存在,也就是说slow一次走一步,fast一次走三步也能相遇。

二、在带环链表中寻找第一个入环的节点

在上图,我们假设slow和fast指针相遇的节点为meet指针,头结点为head指针。(fast指针一次走两步,slow指针一次走一步)

先说方法:meet指针和head指针一次走一步,最后相遇的时候所处的节点就是第一个入环的节点

解析

设meet节点与第一个环节点距离为N,头结点与第一个环节点距离为L,整个环的长度为C。

此时slow指针走的路程为:L+N(注意:当slow和fast指针相遇时,slow指针必定只走了一圈之内。因为slow指针如果走了超过一圈,fast指针必定走了超过两圈,一定在一圈之内就追上了

那么假设fast指针走了x圈,此时fast指针走的路程为:L+x*C+N

此时fast路程为slow两倍:L+x*C+N=2L+2N

                                         L=(x-1)*C+(C-N)

由这个关系式可知:meet指针走的步数是C-N加上环总长度的整数倍,由于环的整数倍长度的影响可以直接忽略,所以可以知道,head指针和meet指针一次走一步,最后必定在第一个入环节点相遇。

代码如下:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode*fast=head;
    struct ListNode*slow=head;
    struct ListNode*meet;
    if(head==NULL || head->next==NULL)
        return NULL;
    else
    {
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;

        if(slow==fast)
        break;
    }
    if(slow!=fast)
    return NULL;
   meet=slow;
   while(meet!=head)
   {
    head=head->next;
    meet=meet->next;
   }
   return meet;
    }
}

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值