如何判断单向链表是否带环,有回环问题。

问题描述:

一个单向链表的结构,其中一个节点通常为保存一个数据的容器object(data)和一个指向下一个保存数据的容器的地址next组成。

 什么叫带环,或者有回环呢?

比如当前节点next指向当前节点之前任意一个节点地址,那么我们说这个单向链表有回环。或者带环。

1.那么如何判断一个单向链表有环呢?

虽然网上有很多博客都说教式的阐述了如何判断是否有环,但我觉得大多都写得很烂。所以我决定自己写一篇。让自己深刻一遍。

我们先思考怎样才算有环,或者有环的条件

我们大脑里要有一个有环的单向链表结构。这里我们看当一个单向链表中只要有两个节点的next指向同一个节点的地址时,是不是就是有环了。如何理解这句话,如下图,绿色线代表一个回环P节点连接点,我们看是不是a1和a2指向同一个P节点时,这个单向链表就是有回环的。

 当我们找出了问题的根本,再来看其他的博客时,就会好理解得多了。

2.为什么其他博客中会说到一个走的快的指针和一个走的慢的指针,当他们指向同一个节点时,就代表这个单向链表有环了?

操场跑道是一个环对吧。当从学校宿舍到跑道的路程当做还未进入环的链表节点,跑道就是链表的回环,一个跑的快,一个跑的慢,跑的慢的将永远追不上跑的慢的,但是一旦进入的跑道,或者链表回环,那么一旦跑的快的超过慢的一个回环的路程后,他们就相遇了。这就能证明这个链表有回环。速度不同,相遇<===>有回环  , 速度不同,不相遇<===>无回环。因为我们先确定两个指针速度不同,那么根据是否相遇 就能确定是否有回环了。这是结论。

3.重中之重!为什么要规定慢的走一步,而快的走两步?而不是慢的一步,快的三步,或者其他?

这算是判断链表是否有环的问题最重要的一个规则了,百分之99的博客只说了结慢的走一步,而快的走两步,并未说为什么,这里我们来思考为什么。如下图

 有6个节点的一个回环。A快指针每次走3步,B指针每次都一步。

当B慢指针进入回环的1位置时,A指针在4位置。

当B慢指针在2位置时,A快指针在1位置。(这就是上图示例的位置)

当B慢指针在3位置时,A快指针在4位置。

当B慢指针在4位置时,A快指针在1位置。

当B慢指针在5位置时,A快指针在4位置。

当B慢指针在6位置时,A快指针在1位置。

当B慢指针在1位置时,A快指针在4位置。(此时已经和刚进入圈的时候位置一样了)

当B慢指针在2位置时,A快指针在1位置。

当B慢指针在3位置时,A快指针在4位置。

如果你细心发现他们永远不会相遇,A与B指针永远不会在同一个位置。

那么为什么呢???

我们还是来思考两个指针走过的节点数相差的节点数量。是不是只有两个指针相差0个节点,6个节点,12个节点,18个....节点的时候他们才会相遇。想象操场,快的比慢的超过1圈的距离,2圈的距离,3圈的距离时,才会相遇。那么如果快的和慢的一个走3步一个走一步,如果开始位置不同,一个1一个2,那么他们永远是(2,5,)(3,8)(4,11)(5,14)(6,17)

相差的数量为:3,5,7,9,11,13,14....大于6的减去回环6,那么他们永远相差3,5,1,3,5,1.....永远不会相差6的倍数个。

4.那么快的走2步,慢的走1步有什么不同?

还是从相差的节点数量来考虑。不论开始相差几个,比如开始进入回环的时候,一个是1一个是5,

那么情况是(1,5,)(2,7)(3,9)(4,11)(5,13)...

相差位置为4,5,6,7,8.....

相信聪明的你已经知道为什么要求快的走2步,慢的走一步了?因为只有这样,不论你的回环是多少节点组成,两个节点相差的距离中间只要不漏掉任何一个相差数额,最后总会等于回环的节点数,而当相差的距离等于回环数的时候就是他们相遇的时刻。就是快的超过慢的一圈或者n圈的时刻。

这就是为什么要求快的走2步,慢的走1步的原因。

其他那些博客写的啥玩意,看也看不懂一堆公式扔给你也不告诉你为什么。气     _(:з」∠)_

既然知道里原理,实现代码就好写了。

Java代码实现:

public class test{
    public boolean hasCycle(ListNode head){
        ListNode slow,fast;//定义快慢指针
        slow=fast=head;//同一起跑线
        while(fast!=null&&fast.next!=null){//当有指针指向null时,说明到尾结点了,说明此链表无环
            slow=slow.next;//走一步
            fast=fast.next.next;//走两步
            if(fast==slow){//相遇
                return ture;
            }
        }
        return false;
    }
}

//单向链表的一个节点
public class ListNode{
    int value;//博客里说装数据的地方
    ListNode next;//下一个节点的地址
    ListNode(int x){//本节点构造器
        value=x;//头节点
        next=null;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值