关于链表,相信点进这篇文章的小伙伴一定了解了不少链表方面的知识。那么关于带环链表,你了解多少呢?那么接下来,让我们一起来学习了解一下带环链表的知识,相信通过本篇文章的学习,求知若渴的你一定能够收获满满。
一.如何判断链表是否为带环链表
我们可以通过下面的图来辅助理解一下:
我们采用快慢指针的方法来判断链表是否带环。慢指针一次走一步,快指针一次走两步。从慢指针开始进入环开始计算:
设慢指针与快指针间隔为N,那么设追上需要走X步,那么:
2X-X=N
X=N
所以如果带环的话,一定追上,并且不会套圈。
那么我们用代码来演示一下:
这样我们就编写了一个判断链表是否为带环链表的函数。
那么这个时候肯定会有同学有疑问,快指针一次只能走两步吗?慢指针一次只能走一步吗?那么接下来,我们不妨让快指针一次走三步,慢指针一次走一步,让我们一起来找一下规律吧!
快指针每次必慢指针快走两步,因此:
2X=N
X=N/2
乍一看只需走N/2即可追上,但是!!!我们不知道N的奇偶,因此N/2不一定是整数,这样就存在多走(即错过的情况)。那么我们具体来看一看N为偶数和N为奇数的情况:
设环的总长度为C,那么二者错过后,快指针与慢指针之间的距离变为了C-1。这个时候,再判断C-1的奇偶。那么我们对快指针一次走三步,慢指针一次走一步这种情况做一个总结:
N为偶数,第一轮追上;
N为奇数,第一轮错过,N变为C-1,C-1为偶数,第二轮追上;C-1为奇数,陷入死循环,永远追不上。
别大意,还是乍一看,这个总结没有问题对不对?!但是,永远追不上的条件是C-1为奇数(即C为偶数)并且N为奇数,那么这个条件存在吗?接下来我们来验证一下:
我们设非环链表长度为L,当两个指针走到如图所示位置时:
slow走了:L
fast走了:L+x*C+C-N(x为fast已走的圈数)
又根据fast走的路程是slow的三倍:
3slow=fast
因此我们得出以下等式
2*L=(x+1)*C-N
等式左边一定是偶数,前提让C为偶数,那么根据这个等式,N不可能是奇数!!!
因此我们得出结论:
N为偶数,第一圈追上;
N为奇数,第二圈追上。
二.寻找带环链表的带环节点
通过上文的学习我们已经学会了如何判断链表是否为带环链表,那么如果一个链表确实为带环链表,如何寻找它的入环节点呢?
我们仍采用快慢指针的方法,先找到相遇节点,废话不多说,上代码!!!
看了代码之后,相信你肯定有不少疑问,为什么要这么写呢?
我们不妨画出二者相遇时的图:
两者相遇时:
slow走了:L+N(不可能是L+X*N,因为不可能套圈)
fast走了:L+X*C+N(X为圈数)
根据 2slow=fast
得出等式:
L=(X-1)*C+C-N
根据这个关系,我们就可以写成上文的代码。meet和head每次走一步,两者必相遇,相遇节点即为入环节点。
其实找出入环节点还有第二种方法,即判断链表是否相交,找出相交节点。
那么如果要采用法二的话,我们首先需要破环:
struct ListNode*newhead=meet->next;
meet->next=NULL;
破环完成后,我们就根据链表找相交节点的思路进行编写代码。
我们将这种做法作为一种拓展,希望同学们能够根据我说的思路自己动手编写一下代码,如果遇到问题,欢迎大家评论区留言或者私信!
那么今天关于带环链表的讲解我们就到这里,相信坚持下来的你一定收获满满了吧!感谢大家一直以来对茶老师的支持,欢迎批评指正,欢迎交流切磋,今天的课程就到这里,我们下期再会!