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

  • Post author:
  • Post category:其他


环的定义

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

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

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

随便找一篇文章:

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;
    }



版权声明:本文为qq_37080133原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。