美文网首页
java判断链表是否有环(两种方式实现)

java判断链表是否有环(两种方式实现)

作者: Bfmall | 来源:发表于2022-01-19 16:46 被阅读0次

    判断链表是否为带环链表

    方法一、快慢指针移动判断

    首先如何判断链表是否有环,这个时候首先需要知道链表是否为空,如果不为空,则继续判断。

    思路:首先定义两个变量,一个fast,一个slow,让fast 每次走两步,slow每次走一步,当fast和slow相遇时,即是该链表存在环结构。如果该链表为无环结构,则当遍历完这个链表时也不会相遇。即是返回false。

    图例如下:
    图中为了说明情况,
    fast指针初始标记为f0,每移动一次加1,如f1,f2,f3....
    slow指针初始标记为s0,每移动一次加1,如s1,s2,s3....

    5660a8da20b37b4a5a820604efb8205.png

    代码实现如下:

    //初始化一个有环的链表Node
    Node nodeA = new Node("A");
    Node nodeB = new Node("B");
    Node nodeC = new Node("C");
    Node nodeD = new Node("D");
    Node nodeE = new Node("E");
    Node nodeF = new Node("F");
    
    nodeA.next = nodeB;
    nodeB.next = nodeC;
    nodeC.next = nodeD;
    nodeD.next = nodeE;
    nodeE.next = nodeF;
    nodeF.next = nodeD;//此时F节点又指向了D节点,已经产生了环状
    
    /**
         * 判断链表是否有环 (快慢指针的方式)
         *                                       <-----------------
         *                                      |                 |
         *          [A]  ->  [B]  ->  [C]  ->  [D]  ->  [E]  ->  [F]
         *
         * 初始指针  f0
         *          s0
         * 第一次            s1       f1
         * 第二次                     s2                f2
         * 第三次                             s3/f3
         * 本例中即第三次遍历就判断出链表有环
         * @param node
         * @return
         */
        private boolean hasCycle(Node node) {
            if (node == null) {
                return false;
            }
            Node fast = node;
            Node slow = node;
            //此字段仅用来记录遍历次数
            int traverseCount = 0;
            while (fast != null && fast.next != null && slow != null) {
                fast = fast.next.next;//移动2步
                slow = slow.next;//移动1步
                traverseCount ++;
                if (fast == slow) {
                    //如果碰面,就代表有环
                    Log.d(TAG, "hasCycle==>有环...traverseCount="+traverseCount);
                    return true;
                }
                Log.d(TAG, "hasCycle==>已经遍历次数...traverseCount="+traverseCount);
            }
            Log.d(TAG, "hasCycle==>无环");
            return false;
        }
    

    方法二:使用Set<>集合记录Node元素,如果有重复元素则认为有环
    代码实现如下:

    /**
         * 通过Set集合记录值的方式,如果有重复的数据,就代表有环
         * @param node
         * @return
         */
        private boolean hasCycle2(Node node) {
            Set<Node> nodeSet = new HashSet<>();
            //此字段仅用来记录遍历次数
            int traverseCount = 0;
            while (node != null) {
                if (nodeSet.contains(node)) {
                    Log.d(TAG, "hasCycle2==>有环...traverseCount="+traverseCount);
                    return true;
                }
                traverseCount ++;
                Log.d(TAG, "hasCycle2==>traverseCount="+traverseCount);
                nodeSet.add(node);
                node = node.next;
            }
            Log.d(TAG, "hasCycle2==>无环");
            return false;
        }
    

    三、扩展:(内容参考:https://blog.csdn.net/weixin_40879743/article/details/90646399
    如果链表有环,找到有环的入口点是哪个节点(如上图,入口点就是节点D,下面要找到这个节点)
    思路:s=vt(路程=速度*时间)用方程思想列等式解
    因为路程知道,速度知道(二倍关系),时间也知道(相等),所以可以列等式求关系。再用代码体现出关系,就可以解决这道题了。

    如图:假设链表是Link fast是快的那个指针,Slow是慢的呢个指针,蓝色是fast走过的路程,绿色是slow走过的路程

    K是环的入口,P是快慢指针相遇的地方
    a,b,c 分别代表三段长度。


    image.png

    所以图就可以变成:


    image.png

    然后,让指针从 相遇点p 和 起始点first 同时遍历,这样由于 c = a , 所以p和first在k相遇,而k就是入口点。

    代码实现如下:

    /**
         * 如果有环,获取相遇点的node
         * @param node
         * @return
         */
        private Node getMeetNode(Node node) {
            if (node == null) {
                return null;
            }
            Node fast = node;
            Node slow = node;
            //此字段仅用来记录遍历次数
            int traverseCount = 0;
            while (fast != null && fast.next != null && slow != null) {
                fast = fast.next.next;//移动2步
                slow = slow.next;//移动1步
                traverseCount ++;
                if (fast == slow) {
                    //如果碰面,就代表有环
                    Log.d(TAG, "hasCycle==>有环...traverseCount="+traverseCount);
                    return fast;
                }
                Log.d(TAG, "hasCycle==>已经遍历次数...traverseCount="+traverseCount);
            }
            return null;
        }
    
        /**
         * 如果有环,获取环出现的入口点
         * @return
         */
        public Node getCycleEntry(Node node) {
            Node meetNode = getMeetNode(node);
            Node p = meetNode;//相遇点元素的指针
            Node first = node;//链表第一个元素的指针
            while(p != first) {
                //两个指针都进行移动,当相等的时候就找到了入口点
                first = first.next;
                p = p.next;
            }
            return p;
        }
    
       /**
        * 将入口点打印出来:
        */
        Node entryNode = getCycleEntry(nodeA);
        Log.d(TAG, "有环的链表入口点Node Value="+entryNode.value);
    

    相关文章

      网友评论

          本文标题:java判断链表是否有环(两种方式实现)

          本文链接:https://www.haomeiwen.com/subject/bdwrhrtx.html