求带环链表的入口的多种解法

方法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了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值