判断单链表是否有环,如何寻找环的入口结点

今天我们来讨论一个有趣的链表问题,判断链表是否有环,如果有环,环的入口结点如何寻找?

1. 如何判断一个单链表是否有环呢?

一个单链表如果有环,那它只可能是这样的,如下图所示:
在这里插入图片描述
不可能是这样的:
在这里插入图片描述
为什么?因为人家是单链表,单链表结点只有一个next指针,一个结点是不可能开出两个分叉的。
在这里插入图片描述
那么如何判断链表是否有环呢?
算法1: 依次遍历链表的每一个结点,把遍历的结点都放在一个集合中,在放入集合之前,如果发现当前结点已经存在,那个说明这个链表有环,并且这个结点也是链表的入口结点,两个问题完美搞定。直接贴代码:

    public boolean hasCircle(ListNode head){
        if(head==null)return false;
        Set<ListNode> set = new HashSet<>();
        ListNode cur = head;
        while (cur!=null){
            if(set.contains(cur))return true;
            set.add(cur);
            cur = cur.next;
        }
        return false;
    }

设链表长度 N N N,那么算法时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( N ) O(N) O(N)

有没有更好的方法?有的。
算法2: 使用快慢两个指针,slow,fast,快指针依次走两步,慢指针依次走一步,如果链表有环,由于快指针一次走两步,所以快指针先进入环中,如下图所示:
在这里插入图片描述
可以得出结论,如果链表有环,则快指针与慢指针一定相遇。一次写出如下代码:

    public boolean hasCircle(ListNode head) {
        ListNode slow = head, fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow==fast)return true;
        }
        return false;
    }

算法空间复杂度显然 O ( 1 ) O(1) O(1),当慢指针走到链表入口的时候,假设快指针距离慢指针 x x x步,可知 x x x小于环的长度,因为慢指针每次走一步,快慢指针的距离就缩短一步,所以当慢指针走 x x x步时,快慢指针相遇,由于 x x x小于环的长度,因此,慢指针此时还没有走完一圈。因此时间复杂度 O ( N ) O(N) O(N)

2. 如何找到环的入口结点?

假设慢指针走到入口结点的时候,走的距离为 l e n len len,设环的长度 L L L,当慢指针走到入口结点的时候,快指针此时一定在环中,假设此时快指针已经在环中走了 n n n圈,并多走了 m m m

因为此时快指针并不一定走了 n n n整圈,当一圈很大的时候, n n n可能为0,当快慢指针在入口相遇的时候, m m m为0,

所以:快指针走的总步数: l e n + n L + m len+nL+m len+nL+m,由于快指针一次走两步,所以快指针走的步数是慢指针步数的两倍;所以: 2 l e n = l e n + n L + m 2len =len+nL+m 2len=len+nL+m,即 l e n = n L + m len=nL+m len=nL+m
在这里插入图片描述
OK,今天的内容就到这里啦,拜拜。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值