链表判断是否有环

给定一个链表,给出下列问题

  • 该链表是否有环
  • 若有环,请找出环的开始处
  • 若有环,判断环的大小
方法:快慢指针

​ 分别定义一个快指针fast和慢指针slow,快指针一次走两步,慢指针一次走一步。如果链表没有环,那么fast最终会指向null;如果链表有环,那么快指针和慢指针最终会相遇。所以,如果最终fast == null,那么判断链表无环;如果有fast == slow,那么链表有环。

证明如下。

**1. 第一步:**快慢指针从头结点出发。如下图所示。蓝色表示快指针fast,红色表示慢指针slow。

**2. 第二步:**慢指针slow走到了环入口,共走了k步。此时快指针fast越过了环入口的步数为delta。因为快指针可能绕着环走了很多圈,所以有k == delta + n * R,其中R为环的大小,n为快指针绕环走的步数。

  1. 第三步:慢指针进入环中。因为快指针每次都比慢指针快一步,所以,快慢指针最后一定会相遇。【证明了必然会相遇。】

  2. 第四步:计算快慢指针相遇位置。因为慢指针在刚进入环时距离快指针delta步,所以快指针还需要比慢指针多走R - delta步才能与慢指针相遇。又因为快指针每次走两步,慢指针每次走一步,根据追及问题,慢指针还需要走R-delta步,也就是在对称位置重合

  3. 第五步:让快指针重新从头结点开始走,速度为一次一步,与慢指针相同。可知,快指针走到环入口时,所需步数为k。刚好,前面证明过,k == delta + n * R,这也是慢指针在环中所走的距离。由快慢指针在环中的相遇位置可知,慢指针此时刚好走到了环入口,并与快指针相遇,此时,找到了环入口。【证明了能找到环入口。】

  1. 第六步:如何求环大小。这个相对简单,在证明链表是否有环的过程中,快慢指针第一次相遇。此后,快指针继续按一次两步的速度走,慢指针按一次一步的速度走,并设置一个计数器count = 0,每走一次加1,。当快慢指针再次相遇时,快指针刚好比慢指针多走了R步,而计数器count == R。【计算了环大小】
// 判断单链表是否有环
public static boolean haveCircle(LinkNode head) {
        LinkNode slow = head;
        LinkNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) return true;
        }
        return false;
    }
// 判断环入口
public static int entranceOfLoop(LinkNode head) {
        if(!haveCircle(head)) {
            throw new RuntimeException("该链表无环");
        }
        LinkNode slow = head;
        LinkNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                fast = head;
                int count = 1;
                while(fast != slow){
                    slow = slow.next;
                    fast = fast.next;
                    count++;
                }
                return count;
            }
        }
        return 0;
    }
// 判断环大小
public static int sizeOfLoop(LinkNode head){
        if(!haveCircle(head)) {
            throw new RuntimeException("该链表无环");
        }
        LinkNode slow = head;
        LinkNode fast = head;
        int count = 0;
        boolean flag = false;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                flag = true;
                if(count!=0) return count;
            }
            if(flag) count++;
        }
        return 0;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值