小编想问大家一个问题,就是如果我们需要进行测试一个单向链表是否存在环,应该使用什么方法才是最好的呢?如果大家还不知道有什么方法的话,那就接着往下面看哟!因为今天小编就要为大家介绍一下:在C语言中单向链表环测试并返回环起始节点的实现方法。
原始方法:
就刚刚小编所提出的问题,也许会有朋友想到了一个非常原始的方法,那就是将链表的数据结构进行改变,然后给每一个节点添加一个名为bool的变量,在还没有进行测试的时候,我们全部都初始化成为false值,接着遍历链表,每当我们访问到一个节点的时候,首先要做的就是测试其bool成员来确定这个节点是否已经被我们访问过了。假如说我们所测试出来的值是为true的话,那么就代表着访问过,就是有环的意思。否则的话,就设置bool成员为true的值,表示访问过的意思,接下来我们还要继续进行测试。
假如我们在不改变数据结构的情况下,我们还有以下一种的解决方案。具体是哪种解决方案呢?请大家留意下面的教程:
第一步:测试是否有环
第一种解决的方案就是先测试一下是否有环。首先我们可以先构建出两个迭代器来进行遍历链表,有一个迭代器每一次移动一个节点,另外一个迭代器则每一次移动两个节点。假如说这两个一快一慢的土鳖迭代器相遇了的话,那就可以证明一件事情,那就是他们两个迭代器在某一个时刻都已经到了同一个节点。这样子的话,我们可以非常的肯定的确是有环存在的。其实最直观的理解就是把这两个土鳖迭代器一快一慢的在长度400米环形跑道上各自选择一个位置,接着同一时间顺时针做死了跑,那么这样子的话,这两个土鳖迭代器总可以相遇的。那么有人就会问小编,到底是为什么呢?原因就是因为有一个迭代器会比另外一个迭代器速度要快。
假如有一些朋友是需要进行严谨证明的话,那么我们还可以这样来理解。具体的理解如下:首先我们想象着在某一个迭代的时刻,这两个土鳖迭代器(在接下来的教程中,小编会将其简单称为土鳖,因为这样会更有亲切感,哈哈)都进入了环,一个土鳖距离环的起始点为i,然而另外一个土鳖距离环起始点为j。这个想象是一定有成立的时刻,因为在跑着跑着的时候,这两个土鳖总会进入环,而且一旦进入了环那就再也出不来了,只可以做死了跑。
接下来我们再大胆的进行假设一下这两个土鳖又相互跑了一会儿,他们居然相遇了,有一个土鳖跑了x步,另外一个土鳖则跑了2x步。假如说这个环一共是长n米的话,换句话来说,也就是速度慢的土鳖需要跑n步才可以跑完一圈。接下来我们就可以得出i+x以及j+2x对于n同余,就说明了一件事,那就是:i+x以及j+2x除以n的余数是一模一样的,可以写成同余等式就是(i+x)=j+2x(modn)。然后我们就可以根据同余加减法性质,让上面的表达式来减去x=x(modm),就可以得到以下的表达式:i=(j+x)(modm)。在这个表达式中,大家可以看到因为x是一个未知数,因此在上面的表达式是一个同余方程,另外的字母i、j通通都是普通整数,非常明显其实这个方程是有解的。就比如说:表达式2=(1+x)(mod5)的一个简单解就是数字1。因此这两个土鳖跑着跑着就一定会相遇的。换句话来说,就是我们上面所检测环的算法是可行的方法,不会发生死循环的情况。
第二步:获取环起始点
好了,基于问题一的分析,我们就可以知道了一件事,那就是速度快的土鳖以及速度慢的土鳖一定会在某一个节点进行相遇的。现在我们就把这个点(相遇的节点)假设成为cross,同一时间我们也将环起始点假设成为start。在这里,有一个十分显然的事实,那就是当两个土鳖相遇的时候,速度慢的土鳖所跑过的路径是速度快土鳖的一半。这样子的话,他们两个在相遇之前,当速度慢土鳖跑了一半时,速度快土鳖就已经经过了相遇点(跨越又或者是落脚)。这样子的话,当速度慢土鳖跑完后半段时,速度快土鳖就已经从相遇的地方开始又跑了一模一样的路程到达了相遇的地点。这个路程的长度就是相等于速度慢土鳖一共跑的长度。
那么现在最神奇的地方要来了,那就是假如说速度慢土鳖从头开始跑时,有另外一个速度慢土鳖从相遇的地方cross开始跑,那么这两个土鳖也一定会在相遇的地方进行相遇,接着我们就将这两个土鳖称为分别为A以及B。当B土鳖走的路程以及速度快土鳖后半段时间走过的路程是完全一模一样的,唯一一个的区别就是他稍微慢了一点而已。
那么现在第二个最神奇的地方又要来了,大家都已经知道速度慢土鳖A以及B土鳖的速度是一模一样的,那么这两个土鳖在相遇地点之前的节奏也是一模一样的,换句话来说,就是他们在相遇地点之前就已经开始相遇了,而且还是以完全一样的速度相伴走到了相遇的地方cross。那么这两个土鳖究竟是从什么时候相遇开始这一段快乐旅程的呢?有人知道答案吗》没错,就是从环起始点start开始相遇的。在这里,我们可以让速度慢土鳖A以及B土鳖从相遇地方倒退,这样我们就可以理解到为什么他们会在start点进行相遇了。
好了,现在我们就已经有了解决方案了,具体的解决方案如下:先让速度慢土鳖A从链表头start开始跑,让另外一个速度慢土鳖从相遇点cross开始跑。有人知道这两个土鳖第一次相遇的地方是哪里吗?正确答案就是——环起始点。
最后,为了可以更加方便大家深入的理解,小编在这里特意给大家带来了C++的代码。具体编程代码,如下图:
小编结语:
在这篇教程中,小编主要是向大家介绍一下在C语言中单向链表环测试并返回环起始节点的实现方法。教程的确是有一点点长,但是总的来说,还是比较详细的,所以大家一定要有点耐心的去看哟!不要半途而废了。
课课家会一直更新编程语言的教程,请继续关注我们的网站:课课家教育。谢谢!