单链表面试题~带环链表的入口点

昨天做了一个小总结,发现单链表这块关于带环链表漏掉了,今天就来总结一下。

要寻找一个单链表的环的入口点,首先得判断一个链表是否带环,那么又怎样判断一个单链表是否带环呢?

首先我们得普及一下环有什么特性,我们知道无环的单链表有首节点和尾节点,那么带环的单链表呢?显然还是有首节点的,但是没有尾节点吧。

如图所示:
这里写图片描述

带环的单链表既然没有尾结点,那么要是遍历的话,就会无限循环,我们就没法通过这样来判断这是带环的,那么我们就要想一个办法让其停下来。

我们知道,运动会三千米赛跑的时候,第一名总会超过最后一名一圈,他们都是从同一个起点开始跑的,最后跑的快的追上了跑的慢的。这就是圈的特性,那么我们是不是也可以使用这一点来判断单链表是否带环。

这里写图片描述

我们定义一个快的指针,让其一步走两个节点,再定义一个慢的节点,一步走一个节点,这样,如果链表带环,那么总有一个地方让这两个指针是相遇的;如果不带环那么快的会先到尾结点。

那么接下来就来实现吧:

typedef struct LinkNode  //节点
{  DataType data;  struct LinkNode *next;  
}LinkNode,*pLinkNode; typedef struct LinkList  //链表
{  LinkNode *pHead;  
}LinkList,*pLinkList;  pLinkNode isCircle(pLinkList plist)  //判断是不是带环链表
{  assert(plist);  if (NULL == plist->pHead)  {  printf("链表为空\n");  return NULL;  }  pLinkNode fast = plist->pHead;  pLinkNode slow = plist->pHead;  while (fast && fast->next)  //如果快的指针先到尾结点,那么不是。(奇偶){  fast = fast->next->next;  slow = slow->next;  if (fast == slow)  //相遇,那么是带环的链表return fast;  }  return NULL;  
}  

判断是不是带环的链表很简单,只要理解了这个过程,没啥问题,那么知道了链表是带环的链表,如何来找带环链表的入口呢?

这涉及不太高深的数学问题,作为数学专业的我,还是很乐意讲解的,首先来看下图:

这里写图片描述

我们先将一些未知量设出来,这样就可以进行运算了:

首先可以列出一个简单的等式:a+r=L ①

那么再根据两个指针来列一个等式: a+n*r+x=(a+x)*2 ②

解释一下第二个式子:左半部分是快的指针相遇前走的路程首先走了一段 a 之后,在进入环中走了 n 圈,又走了x段路程和慢的指针相遇;慢的指针路程因为它的速度是快的指针的一半,所以时间相同的情况下,快的指针走过的路程当然是慢的指针走过的路程的二倍。

然后联合①、②得:a=n*r-x

这样假设n为1时,即——a=r-x,我们要是再定义一个指针,从表头开始遍历,同时,让慢的指针从相遇点开始,那么当他们相遇的时候,就是入口点的地方。

下面我们来看代码:

pLinkNode firstCrossNode(pLinkList plist)  
{  assert(plist);  if (NULL == plist->pHead)  {  printf("链表是空\n");  return NULL;  } pLinkNode ret = isCircle(plist);  //接收相遇点的指针if (ret == NULL)  {  printf("链表不带环\n");  return NULL;  }   pLinkNode start = plist->pHead;  //从表头开始pLinkNode slow = ret;  //从相遇点开始while (start)  {  start = start->next;  slow = slow->next;  if (start == slow)  return start;  //返回入口点的指针}  
} 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部