方法1:
公式推导:定义两个指针,一个快指针,一个慢指针。两个指针一起走,快指针每次走两步,慢指针每次走一步(相当于速度不一样)。如果有环,那么它们就一定会相遇,当他们相遇时,走的次数是一样的(相当于时间),路程不一样。且,快指针的路程是慢指针的路程的2倍。这就的到了图中的第一个公式。将第一个公式进行一系列的化简,得出最后一个公式。
通过最后一个公式我们可以知道,如果有两个指针同时走,一个从相遇点出发(相当于少走了y),一个从链的起点出发,那么,它们一定会在环的入口点相遇的。
pNode GetCircleEntryNode(pNode meet, pList plist)//方法1
{
pNode first = meet;//从相遇点出发
pNode second = plist;//从起点出发
if ((plist == NULL) || (meet==NULL))//相遇点为NULL,代表没有环
{
return NULL;
}
while (first!=second)
{
first = first->next;
second = second->next;
}
return first;
}
方法2:
定义两个指针,第一个指针先走环的长度步,然后两个指针一起走,当他们相遇的时候停下来,相遇点便是入口点。
pNode GetCircleEntryNode(int len,pList plist)//方法2
{
pNode first = plist;
pNode second = plist;
if ((plist == NULL)||(len==0))//环的长度为0,说明没有环
{
return NULL;
}
while (len-->0)//第一个指针先走环的长度步
{
first = first->next;
}
while (first!=second)
{
first = first->next;
second = second->next;
}
return first;
}
方法3:
从相遇点断开,就相当于有交点的两条链。将两条链的长度len1,len2分别求出来,用k来表示长度之差,哪个长一点,哪个就先走K步,K步走完后,长的链剩下的长度和短的链一样长,那么,它们到交点(环的入口)距离一样远,现在同时走,相遇点便是入口点。
pNode GetCircleEntryNode(pNode meet, pList plist)//方法3
{
pNode first = meet;
pNode second = plist;
pNode cur = meet;//cur指针用于记住相遇点的位置,方便后边从此断开
int len1 = 0;
int len2 = 0;
if ((plist == NULL) || (meet == NULL))
{
return 0;
}
first = first->next;//让first指针指向相遇点的下一个位置
cur->next = NULL;//将链表从相遇点断开,链表变成两条有交点的链
cur = first;//cur记住L1的起始位置,方便下一次便利的时候可以找到头
while (first!=NULL)//遍历L1,求出其长度len1
{
++len1;
first = first->next;
}
while (second != NULL)//遍历L2,求出其长度了len2
{
++len2;
second = second->next;
}
first = cur;//让first指针指向链L1的起点
second = plist;//让second指针指向链L1的起点
if (len1 > len2)//比较两条链的长度,两条链哪条长哪条先走
{
int k = len1 - len2;
while (k-- > 0)
{
first = first->next;
}
while (first != second)//让两个指针同时往后走走,指向同一点及交叉点时停下
{
first = first->next;
second = second->next;
if (first == second)
{
return first;
}
}
}
else
{
int k = len2 - len1;
while (k-- > 0)
{
second = second->next;
}
while (first != second)
{
first = first->next;
second = second->next;
if (first == second)
{
return first;
}
}
}
}
下边是相关函数的实现和测试代码
头文件和函数声明LinkNode.h部分
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node* next;
}Node, *pNode, *pList;
void InitLinkList(pList* pplist); //初始化
void PushFront(pList*pplist, DataType x);//前加
void Display(pList pl); //打印
void DestroyList(pList* pplist);//销毁
pNode Find(pList pl, DataType x); //查找函数
pNode CheckCycle(pList plist);//判断是否有环
int GetCircleLength(pNode meet);//求环的长度
pNode GetCircleEntryNode(pNode meet, pList plist);//求环的入口
//pNode GetCircleEntryNode(int len,pList plist)
#endif //__LINKLIST_H__
各个函数的具体实现LinkNode.c部分
#include "LinkNode.h"
void InitLinkList(pList* pplist)//初始化
{
assert(pplist != NULL);
*pplist = NULL;
}
void PushFront(pList*pplist, DataType x)//前加
{
assert(pplist != NULL);
pNode node = malloc(sizeof(Node));
if (node == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}
node->data = x;
node->next = NULL;
node->next = *pplist;
*pplist = node;
}
void Display(pList pl)//打印
{
if (pl == NULL)
{
return;
}
while (pl != NULL)
{
printf("%d-->", pl->data);
pl = pl->next;
}
printf("\n");
}
void DestroyList(pList* pplist)//销毁
{
assert(pplist != NULL);
while (*pplist != NULL)
{
pNode tmp = (*pplist)->next;
free(*pplist);
(*pplist)->next = NULL;
*pplist = tmp;
}
}
pNode Find(pList pl, DataType x) //查找函数
{
pNode head = pl;
while (head->data != x)
{
head = head->next;
}
if (head->data == x)
{
return head;
}
else
{
return NULL;
}
}
//判断是否有环
pNode CheckCycle(pList plist)
{
pNode fast = plist;
pNode slow = plist;
if (plist == NULL)
{
return NULL;
}
while ((fast != NULL) && (fast->next != NULL))
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return fast;
}
}
return NULL;
}
//求环的长度
int GetCircleLength(pNode meet)
{
pNode Meet = NULL;
int count = 0;
if (meet == NULL)
{
return count;
}
Meet = CheckCycle(meet);
meet = Meet;
Meet = Meet->next;
count++;
while (Meet != meet)
{
count++;
Meet = Meet->next;
}
return count;
}
//求环的入口
//pNode GetCircleEntryNode(pNode meet, pList plist)//方法1
//{
// pNode first = meet;//从相遇点出发
// pNode second = plist;//从起点出发
// if ((plist == NULL) || (meet==NULL))//相遇点为NULL,代表没有环
// {
// return NULL;
// }
// while (first!=second)
// {
// first = first->next;
// second = second->next;
// }
// return first;
//}
//
//pNode GetCircleEntryNode(int len,pList plist)//方法2
//{
// pNode first = plist;
// pNode second = plist;
// if ((plist == NULL)||(len==0))//环的长度为0,说明没有环
// {
// return NULL;
// }
// while (len-->0)//第一个指针先走环的长度步
// {
// first = first->next;
// }
// while (first!=second)
// {
// first = first->next;
// second = second->next;
// }
// return first;
//}
pNode GetCircleEntryNode(pNode meet, pList plist)//方法3
{
pNode first = meet;
pNode second = plist;
pNode cur = meet;//cur指针用于记住相遇点的位置,方便后边从此断开
int len1 = 0;
int len2 = 0;
if ((plist == NULL) || (meet == NULL))
{
return 0;
}
first = first->next;//让first指针指向相遇点的下一个位置
cur->next = NULL;//将链表从相遇点断开,链表变成两条有交点的链
cur = first;//cur记住L1的起始位置,方便下一次便利的时候可以找到头
while (first!=NULL)//遍历L1,求出其长度len1
{
++len1;
first = first->next;
}
while (second != NULL)//遍历L2,求出其长度了len2
{
++len2;
second = second->next;
}
first = cur;//让first指针指向链L1的起点
second = plist;//让second指针指向链L1的起点
if (len1 > len2)//比较两条链的长度,两条链哪条长哪条先走
{
int k = len1 - len2;
while (k-- > 0)
{
first = first->next;
}
while (first != second)//让两个指针同时往后走走,指向同一点及交叉点时停下
{
first = first->next;
second = second->next;
if (first == second)
{
return first;
}
}
}
else
{
int k = len2 - len1;
while (k-- > 0)
{
second = second->next;
}
while (first != second)
{
first = first->next;
second = second->next;
if (first == second)
{
return first;
}
}
}
}
测试函数test.c部分
#include "LinkNode.h"
void test()
{
pList list;
int len = 0;
pNode meet = NULL;
pNode ret1 = NULL;
pNode ret2 = NULL;
pNode ret3 = NULL;
InitLinkList(&list);
PushFront(&list, 1);
PushFront(&list, 2);
PushFront(&list, 3);
PushFront(&list, 4);
PushFront(&list, 5);
PushFront(&list, 6);
PushFront(&list, 7);
PushFront(&list, 8);
PushFront(&list, 9);
Find(list, 1)->next = Find(list, 5);
meet = CheckCycle(list);
len = GetCircleLength(meet);
//验证方法1
//ret1 = GetCircleEntryNode(meet,list);
/*if (ret1 == NULL)
{
printf("NOCYCLE\n");
}
else
{
printf("the entry is:%d\n",ret1->data);
}*/
//验证方法2
ret2 = GetCircleEntryNode(len, list);
if (ret2 == NULL)
{
printf("NOCYCLE\n");
}
else
{
printf("the entry is:%d\n", ret2->data);
}
//验证方法3
/*ret3 = GetCircleEntryNode(meet, list);
if (ret3 == NULL)
{
printf("NOCYCLE\n");
}
else
{
printf("the entry is:%d\n", ret3->data);
}*/
}
int main()
{
test();
return 0;
}
PS:想要验证哪种方法就就哪种方法的函数和测试代码放开就OK了。