重新开始战斗16-编程之美-判断链表是否有环

申明:

这个题目并不是编程之美收录的题目,但是笔者在面试过程中,遇到过至少两次,其中一次就是创新工场。因此,笔者感觉有必要在这里和大家分享一下这个题目。

 

问题描述:

一个链表(只有next和data两个域),判断其是否存在环。(环入口可能存在任何位置,并不是只有头结点)。如果存在环,并求解环的入口位置。

 

问题一:

对于问题一,在面试创新工场的时候,由于笔者自己实力有限,并没有给出完全正确的代码。其实问题一的求解可以用生活中的例子来解决——两个人在操场上跑步。将链表看成跑道,如果该跑道就是一条直线,那么两个人跑步,跑得快的人永远不可能在超过慢者后再次和慢者相遇,如果跑道是环状的,那么快着必然在超越慢者后再次和慢者相遇。

基于这种思想,我们设置两个指针p1和p2,初始情况下,同时指向链表的头结点,p1以一个节点单位进行移动,p2以两个节点为单位进行移动。如果链表没有环,那么任意一个节点最后都将指向NULL,而如果存在环,在环中,一定存在p1和p2同时指向同一节点。

这时,有同学可能要问,对于跑步,一定相遇是很好理解的,但是对于p1和p2的移动,为什么当有环存在时,一定会相遇呢,难道不会刚好错开吗?其次,为什么p2节点要以两个节点单位进行移动,而不是三个,甚至更多?

p1以一个节点单位移动,p2以两个节点单位移动,那么他们一定相遇!试想一下,如果链表存在环,在某一时刻,p1和p2都进入了环(p2肯定比p1先进入),由于p2相对于p1的移动速度多一个节点单位,因此将可以将p1看出没有移动,p2以一个单位移动,显然,p2最后必然和p1相遇,而且p1最多遍历环一圈。如果p2相对于p1的移动速度快两个节点单位,那么有可能出现p2跳过p1的情况,同理,p2以更快的速度移动的情况。

具体伪代码如下:

bool circleexist(Node* head)

{

         If(head== NULL)

                   returnfalse;

 

         Node*p1=head;

         Node*p2=head;

        

         While(p1)

         {

                   p1=p1->next;

                   p2=p2->next->next;

                   if(p1== p2)

                            returntrue;

         }

         returnfalse;

}

 

问题二:

判断环的入口,这个问题说白了就是个数学问题。知道这样的一种结论,即“在环中相遇点到入口的距离等于头结点到入口的距离”就能很快的解决问题二,关于这个结论证明过程如下:

当链表存在环时:

设p1和p2在环中相遇,相遇点距离入口点的距离为x,头结点到环入口点的距离为y,那么p1节点所有的距离d1=x+y。

从问题一中发现,p2节点在和p1相遇时,比p1节点多移动了环一圈的距离r,又p2节点的移动速度是p1的两倍,因此,p2节点移动的距离d2=2*d1=2x+2y=x+y+r。

即从2x+2y=x+y+r得到x+y=r。

X=r-y。

证得。

有了这个结论后就可以让两个指针从头结点和相遇点同时出发,相遇的地方即环的入口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值