关于链表的相交、环判断问题

这段时间玩的有点多,好几周没写了,中间落下了好多内容没有总结,今天之后陆续抽时间补起来,不过学校的课也都相应的要结束,面临考试,所以速度会慢些。其余的不多说,这次先总结一下,比较常见的关于链表的相交、环的问题。


第一个是普通的环检测问题,判断一个链表是否有环,解题思路就是,定义两个指针,一个快指针一个慢指针,这两个指针在这条链表跑道跑,慢的指针速度为1,快的指针速度为2,若是这两个指针最后相遇,则说明有环,因为有环的话,快指针必然会追上慢指针,
这里我的链表结构体如下

typedef struct Node
{
	int data;
	struct Node *next;
}Node;

typedef Node *List;

环判断实现如下:

Node *Ring(List plist)//判断链表是否有环,返回相遇点
{
	assert(plist != NULL);
	Node *p = plist;//慢指针
	Node *q = plist->next;//快指针
	while(q->next != NULL && q != p)//当快指针走到尾结点或者两指针相遇,循环结束
	{
		p = p->next;//慢指针速度1
		if(q->next!=NULL)//防止越界,q->next存在才进行两步的前进
		{
			q = q->next->next;//快指针速度2
		}
	}
	if(q->next == NULL)//循环结束,若是快指针走到尾,则无环
	{
		return NULL;
	}
	return p;//否则返回相遇点
}

第二个问题是检测两链表是否相交,先做最简单判断,仅判断是否相交,返回bool值,因为链表相交和平常认知的相交有所区别,下边简单说明一下在这里插入图片描述
如图说明,若是两个链表相交,则在交点之后一直重合,所以最简单的判断方法就是两直链表走到尾巴,判断尾巴是否相同,然后返回真假,具体实现如下

bool Banana(List plistA,List plistB)//仅判断俩无环单链表是否相交
{
	assert(plistA != NULL);
	assert(plistB != NULL);
	Node *p1 = plistA;
	Node *p2 = plistB;
	while(p1->next != NULL)
	{
		p1 = p1->next;//找第一个尾巴
	}
	while(p2->next != NULL)
	{
		p2 = p2->next;//找第二个尾巴
	}
	return p1 == p2;
}

更好一点的设计就是返回交点,这个时候思路就要发生改变了,若是相交返回交点,否则返回空指针,函数名变为Node *Cross(List plistA,List plistB);,这个时候我们先假设两链表都没有环,这样若是两个链表长度不同,我们通过内部函数获得链表长度,定义两个指针初始化尾两链表头,让长的链表先走俩链表差值步数,此时的两个链表再同时走,若是链表走到尾或者走到相同的地方,则停止前进,停止后判断当前是否走到尾,走位说明不相交,否则当前停止的地方就是交点位置,返回即可,大致如图表示
在这里插入图片描述
代码实现如下:

Node *Cross(List plistA,List plistB)//两个无环的链表判断是否相交,返回交点地址
{
	assert(plistA != NULL);
	assert(plistB != NULL);
	int len_a = GetLength(plistA);//获得A长度
	int len_b = GetLength(plistB);//获得B长度
	int t = len_a - len_b;//获得差值
	Node *p = plistA;
	Node *q = plistB;//初始化前进指针
	if(t > 0)
	{
		for(int i=0;i < t;++i)
		{
			p = p->next;
		}
	}
	else
	{
		for(int i=0;i < (-t);++i)
		{
			q = q->next;
		}
	}//长的链表先走
	while(p != q && p != NULL && q != NULL)//相遇或者走到尾结束前进
	{
		p = p->next;
		q = q->next;
	}
	if(p == NULL)//若是走到尾
	{
		return NULL;
	}
	return p;//否则返回交点
}

然后就是复杂于直链表的相交了,带环的链表相交,大致情况如图所示:
在这里插入图片描述
第一种,一个无环一个有环,则两链表必然不相交,第二、三个都有环,就要另作讨论。
以第三个为例,我们把环打开,再将链表当做普通链表处理,最后还原即可,如图在这里插入图片描述
所以这次的相交判断就比较完全,实现如下:

Node *RingCross(List plistA,List plistB)//链表相交,若相交返回交点,否则返回NULL
{
	assert(plistA != NULL);
	assert(plistB != NULL);
	Node *p = Ring(plistA);//环内获得相遇点
	Node *q = Ring(plistB);//环内获得相遇点
	if((p != NULL && q == NULL)||(p == NULL && q != NULL))//若是一个为环,一个非环,则不相交
	{
		return NULL;
	}
	else if(p == NULL && q == NULL)//俩个都非环,调用非环链表相交处理的函数
	{
		return Cross(plistA,plistB);
	}
	else//否则情况为两链表都有环
	{
		Node *p_a = p->next;//
		Node *p_b = q->next;//开环准备,将开环点后的内容先保存起来
		p->next = NULL;
		q->next = NULL;//两环开环
		Node *c = Cross(plistA,plistB);//开环后当非环链表处理
		p->next = p_a;
		q->next = p_b;//还原链表
		return c;
	}

到这里链表相交基本完成


最后是环检测的优化,我们判断链表是否有环,若有环返回入环第一个点,无环返回空指针,放在最后说是因为这里我们用到了相交的思想,若是有环,在环内相遇点处断开,断开处的后继保存,当作新的链表头,此时生成两个链表,我们获得两链表的相交点,即为原链表的入环第一个点
如图说明在这里插入图片描述
这里绿色为新的B链表,黄色为新的A链表,可以看到原带环链表的入环第一个点为现在的AB链表交点,所以此时问题变成获得交点问题,所以调用之前的相交函数即可,实现如下

Node *RingFirst(List plist)//获得入环的第一个点,无环返回空指针
{
	assert(plist != NULL);
	Node *p1 = Ring(plist);//获得环内相遇点
	if(p1 == NULL)//若是返回的点为NULL说明无环,返回结果
		return NULL;
	Node *p2 = p1->next;//相遇点保存
	p1->next = NULL;//开环
	Node *p3 = Cross(plist,p2);//找到开环后两条链表的交点
	p1->next = p2;//还原链表
	return p3;//返回入环第一个点
}

这里关于链表换问题,交点问题基本结束,我去休息啦~~,看完的不胜感激,本事还是菜中菜,不喜勿喷,有更棒方法交流的欢迎移步留言,感谢观众老爷们❤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值