码迷,mamicode.com
首页 > 其他好文 > 详细

判断链表是否有环及入口点

时间:2016-05-12 22:04:47      阅读:175      评论:0      收藏:0      [点我收藏+]

标签:

判断链表是否有环方法——快慢指针

一、 问:如何判断是否有环?

答:如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,快的指针总会超过慢的指针一圈。

设置两个指针(fast,slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。(当然,fast先行头到尾部为NULL,则为无环链表)。

 

#include <stdio.h>
typedef struct ListNode
{
  int val;
  ListNode *next;
};
//判断链表是否有环(C++实现)
bool isloop(ListNode *pHead)
{
  ListNode *fast=pHead;
  ListNode *slow=pHead;
  //链表长度为奇数,则fast->next为空,否则,fast为空
  while(fast!=NULL&&(fast->next!=NULL))
  {
    fast=fast->next->next;
	slow=slow->next;
	
	//如果有环,则两个指针在环中某一点相遇
	if(fast==slow)
	{ break; }
  }
  
  if(fast==NULL||fast->next==NULL)
     return false;
  else
     return true;
  
}

附其他方法:

法1,遍历链表。

  将遍历过的节点放在一个hash表中。如果一个节点已经存在hash表中,说明链表有环。时间复杂度O(n),空间复杂度O(n)。

法2,反转链表。

二、计算环的长度

//计算环的长度:从第一次相遇开始计数,到第二次相遇,走的长度即为环的长度
int len_loop(ListNode *pHead)
{
  if(isloop(pHead)==false)
     return 0;
  ListNode *fast=pHead;
  ListNode *slow=pHead;
  int len=0;
  bool begin=false;
  bool again=false;
  while(fast->next!=NULL&&fast!=NULL)
  {
    fast=fast->next->next;
	slow=slow->next;
	
	//超一圈后开始计数
	if(fast==slow&&(again==false))
	{
	  begin=true;
	  again=true;
	}
	//超过两圈停止计数,跳出循环
	if(fast==slow&&(again==true))
	    break;
		
	//计数
	if(begin==true)
	  ++len;
  }
   return len;
}


三、问:如何判断环的入口点?

答:碰撞点p到连接点的距离=头指针到连接点的距离,所以,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。

 

为什么呢?需要一个简单的计算过程:

(1)当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈。假设slow走了s步,那么fast走了2s步。fast的步数还等于s走的加上环里转的n圈,所以有:

2s = s + nr。因此,s = nr。

(2)设整个链表长为L,入口据相遇点X,起点到入口的距离为a。因为slow指针并没有走完一圈,所以:

a + x = s,带入第一步的结果,有:a + x =nr = (n-1)r + r = (n-1)r + L - a;即:

a = (n-1)r + L -a -x;

这说明:从头结点到入口的距离,等于转了(n-1)圈以后,相遇点到入口的距离。因此,我们可以在链表头、相遇点各设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

//计算环的入口结点
Node* findLoopEntrance(ListNode *pHead)
{
  ListNode *fast=pHead;
  ListNode *slow=pHead;
  while(fast->next!=NULL&&fast!=NULL)
  {
    fast=fast->next->next;
	slow=slow->next;
	//到达相遇点
	if(fast==slow)
	{  break; }
    
  }
  if(fast==NULL||fast->next==NULL)
    return NULL;
   slow=pHead;//slow指针从头结点开始遍历
   
   while(slow!=fast)
   {
     slow=slow->next;
	 fast=fast->next;
   }
   
   return slow;
}
附:

如何判断两个链表(不带环)是否相交?将其中的一个链表首尾相连,然后判断另一个链表是否带环即可,而检测出来的依赖环入口即为相交的第一个点。



判断链表是否有环及入口点

标签:

原文地址:http://blog.csdn.net/xiaominkong123/article/details/51354248

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!