算法实验室-20-单链表的环(判断环,求环长度,求环的入口)

环的定义

确实,如果没有接触过环的概念,那么第一印象就是一个圈,也就是如果出现一个题,询问环的入环节点时

通常会很懵逼的:一个完整的圈,哪哪都是入口,这道题有病?

直到...你看了环的相关原理之后,你才会恍然大悟:哦,原来是这样。

随便找一篇文章:https://blog.csdn.net/lym940928/article/details/88878425

我们直接来看一张图:

也就是说,“环”并不一定就是一个圈,可能还带着个尾巴。

详细定义: 就看图呗,反正我没找到,反正楼上这张图统称为带环问题。

对于带环问题,通常比较关注的几个角度分别是:

1.判断是否为环

2.求环长度

3.判断环的入口

对于第一个问题,判断是否为环,典型的方案就是快慢指针的方案,我比较喜欢称为刘翔超圈问题。

也就是你和刘翔一起跑圈,你们一定会照面几次。原因就是刘翔超圈了。

先假设构造一个环:

public class ListNode {
    public int val;
    public ListNode next;
    public String sVal;
    public ListNode(String sVal){
        this.sVal = sVal;
    }
    public ListNode(){}
}

public static ListNode createCircle(){

        ListNode A = new ListNode("A");
        ListNode B = new ListNode("B");
        ListNode C = new ListNode("C");
        ListNode D = new ListNode("D");
        ListNode E = new ListNode("E");
        ListNode F = new ListNode("F");
        ListNode G = new ListNode("G");
        ListNode H = new ListNode("H");
        A.next = B;
        B.next = C;
        C.next = D;
        D.next = E;
        E.next = F;
        F.next = G;
        G.next = H;
        //环
        H.next = C;
        return A;
    }

双指针判定这个环究竟是不是:还是比较简单的,快慢指针在存在环的单链表里面,一定会相遇

    public boolean judgeCircleExit(ListNode head){

        ListNode lower = head;
        ListNode faster = head;

        while (faster!= null&&faster.next!= null&&faster.next.next!= null){
            lower = lower.next;
            faster = faster.next.next;
            if(lower.equals(faster)){
                return true;
            }
        }
        return false;
    }

另外一个问题:求环长

1.先确定第一个相遇的位置为0,然后再次相遇时,显然,faster走的路程是lower的两倍,换个角度想:当再次相遇时,faster比lower多跑了一圈

    /**
     * 求环长度
     * @param head
     * @return
     */
    public int countCircleLen(ListNode head){

        ListNode lower = head;
        ListNode faster = head;
        /**
         * statu = 0:未相遇
         * statu = 1:第一次相遇
         * statu = 2:第二次相遇(结束)
         */

        int statu = 0;
        int len = 0;
        while (faster!= null&&faster.next!= null&&faster.next.next!= null){
            lower = lower.next;
            faster = faster.next.next;
            if(lower.equals(faster)){

                Debug.Log(statu);
                statu++;
                if(statu == 2){
                    break;
                }
            }
            if(statu==1){
                len++;
            }
        }
        return len;
    }

求环的入口

这是一个数学题...

https://blog.csdn.net/qq_36781505/article/details/91401474

解决思路也比较简单,参考它的代码就行

    public ListNode getEntrance(ListNode head){
        ListNode faster = head;
        ListNode lower = head;
        while (faster!= null&&faster.next!= null){
            lower = lower.next;
            faster = faster.next.next;
            if(lower == faster){
                //第一次相遇在p1点
                break;
            }
        }
        //让faster回到p0点
        faster = head;
        //经过m圈后,faster和lower(以步长为1行走)将会在经过m圈后在
        // p3也就是entrance点相遇
        while (faster!= lower){
            lower = lower.next;
            faster = faster.next;
        }
        return faster;
    }

嗯,然后来找bug

1.上述代码默认了一定存在环的情况,而如果环不存在的话,那么得返回null是吧

显然的,上面没有返回

判断不存在环,只要不相遇就成


    /**
     * 求环的入口
     * @param head
     * @return
     */
    public ListNode getEntrance(ListNode head){
        ListNode faster = head;
        ListNode lower = head;
        boolean flag = false;
        while (faster!= null&&faster.next!= null){
            lower = lower.next;
            faster = faster.next.next;
            if(lower == faster){
                //第一次相遇在p1点
                flag = true;
                break;
            }
        }
        //不相遇的情况
        if(!flag){
            return null;
        }

        //让faster回到p0点
        faster = head;
        //经过m圈后,faster和lower(以步长为1行走)将会在经过m圈后在
        // p3也就是entrance点相遇
        while (faster!= lower){
            lower = lower.next;
            faster = faster.next;
        }
        return faster;
    }

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值