链表中的环的问题

  最近遇到了一些关于链表中的环的问题,就好好研究整理了下。环是链表中的尾结点指向前面的某一结点(可以是头结点也可以是中间的结点)。其实用 0 6 来形容两种环,很形象,前者尾指向头结点,后者尾指向中间结点。

 一般的环相关的问题:

 

1.  判断是否有环

利用快慢指针来判断。假设p1一次走一步,而p2一次走n(n>1)步。当p2NULL或者p1p2指针相同时,停止循环,进行判断,如果指针相等,则存在环。

证明:这里假设p1走的距离为s,那么p2走的为n*s。如果循环结束时p2NULL了,显然没有环;另一种循环结束情况为p1p2相等了,也就是两指针相遇了。慢的可以追上快的,说明有回路()

假设环长为r,这里p2走了x圈然后在相遇点和p1相遇,即s+xr = ns  =>  (n-1)s= xr  .很多文章都以p2走两步来判断环,其实不是绝对的,这里n>1p2p1快即可。而且对于部分文章说的第一次相遇p2p1快一圈的也不正确,这里就算n=2s=xr,只有当s =r 也就是在相遇时,恰好p1走的等于环长,才是p2p1多一圈。nr,都是视情况而定,取整符合公式即可。但要注意,因为相遇肯定在环里面。

下面为判断环是否存在以及求环入口的代码(来自牛客题我写的通过的):

<span style="font-size:14px;">/*
struct ListNode {
   int val;

   struct ListNode *next;

   ListNode(int x) :

       val(x), next(NULL) {

    }
};

*/

class Solution {
public:

   ListNode* EntryNodeOfLoop(ListNode* pHead)

    {

        ListNode* p1=pHead;

        ListNode* p2=pHead;

         ListNode* p3=pHead;

        if(pHead==NULL||pHead->next==NULL)

         return NULL;

        do{

        p1=p1->next;

        p2=p2->next->next;

    }while(p2!=NULL&&p2->next!=NULL&&p1!=p2);

  if(p1==p2)        //到此,p1==p2则存在环

      {
         //确定存在环之后,返回环的入口点

    while(p3!=p1)  

      {

     p3=p3->next;

     p1=p1->next;

    }

    return p1;        //这里返回环的入口点

   }
   return NULL;

    }
};
</span>


2.确定存在环之后,求环的入口点

求环入口,为了方便,让n=2(即快的一次两步,慢的一次一步)。用两个指针,p1从相遇点继续出发一次一步,另外一个指针从头出发一次一步,两个指针在环入口相遇。

证明:为了方便可以n=2,但不同的环,r s 都是不固定的。于是上面的s+xr = ns  =>  s=xr  s = L(头到环入口)+Y(环入口到相遇点)所以L+Y = xr  =>  L=xr –Y =(x-1)r+r-Y.  这里不管x 是多少圈,等式左边的L就是p3从头跑的距离;右边p1先跑个r-y的距离,也就是刚好从相遇点跑到了环入口(Y+r-Y=r),再加上(x-1)*r圈,所以p1还是在环入口处,而p1 的距离和p3的相同(速度一样),所以刚好p1p3相遇点就是入口点。

注意:很多博客说多一圈,或者这里x1,都是错的。它的原理在于不管多少圈都可以

代码实现在上面代码里加了。


3.求环的长度(不依赖于找到环的入口)

p1p2的相遇点开始跑,再次回到p2位置就是环长(因为快慢相遇相遇肯定在环中);或者确定好环的入口点之后,将p3重新从p1跑,回到p1处(p1此时在环入口),也可以记录一圈

<span style="font-size:14px;">p3 = p1->next;

int num=0; //num为环中不包括环入口点的所有点个数

while(p3!=p1){

 ++num;

 p3=p3->next;

}
</span>


4.求链表长

从头跑到入口处p1处,得到L的长度,然后步骤3得到r 的长度,L+r 就是链表长度,和3一样遍历就可以,就不重复写了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值