【Java实现数据结构】链表成环的一些检测与操作

正文之前

今天早上看简书的时候,发现了一个写的很好的Java实现的数据结构的Repo,所以就干脆对着学了起来。感觉还行,现在丢一点学习成果。地址是:https://github.com/buptdavid/datastructure

《【Java实现数据结构】链表成环的一些检测与操作》

正文

如果你要测试,直接复制下面所有除了Node.java之外的代码,丢到一个文件LinkedListLoop.java里面,辅助文件Node.java如下,新建这个文件然后把两个java文件丢到一个文件夹下就OK:

//Node.java
public class Node<T> {
    public Node<T> next;
    public T data;
    
    public Node(T _data){
        data = _data;
    }
}

下面所有的都是LinkedListLoop.java里面的内容。

/**
 * 1. 判断一个链表是否存在环儿<br>
 * 2. 如果有环儿计算环儿的长度<br>
 * 3. 找出环儿的连接点<br>
 * @author weijielu
 * @see LinkedListLoopTest
 */
public class LinkedListLoop{
    
    /**
     * 判断一个链表是否存在环儿
     * @param header
     * @return 是否存在环儿
     */
    public static boolean isExistLoop(Node header){
        // 定义两个指针fast和slow,fast移动步长为2,slow移动步长为1
        Node fast = header;
        Node slow = header;
        
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            
            //如果相遇则存在环儿,跳出
            if(fast == slow){
                break;
            }
        }
        
        // 根据跳出循环的条件return
        if(fast == null || fast.next == null){
            return false;
        }else{
            return true;
        }
    }
    
    /**
     * 计算有环儿链表的环儿长度<br>
     * fast, slow从碰撞点出发再次碰撞就是环儿的长度
     * @param header
     * @return 返回环儿的长度
     */
    public static int loopLength(Node header){
        // 如果不存在环儿,返回0
        if(!isExistLoop(header)){
            return 0;
        }
        
        Node fast = header;
        Node slow = header;
        int length = 0;
        boolean begin = false;
        boolean again = false;
        
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            
            // 超过两圈后停止计数,跳出循环
            if(fast == slow && again == true){
                break;
            }
            
            // 超过一圈后开始计数
            if(fast == slow && again == false){
                begin = true;
                again = true;
            }
            
            if(begin == true){
                ++length;
            }
        }
        
        return length;
    }
    
    /**
     * 找出环儿的连接点<br>
     * 碰撞点到连接点的距离=头指针到连接点的距离<br>
     * 因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点<br>
     * @param header
     * @return 环儿连接点
     */
    public static Node findLoopEntrance(Node header){
        Node fast = header;
        Node slow = header;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        
        if(fast == null || fast.next == null){
            return null;
        }
        
        slow = header;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        
        return slow;
    }
    public static void main(String[] args) {
        Node<Integer> node1 = new Node<Integer>(2);
        Node<Integer> node2 = new Node<Integer>(2);
        Node<Integer> node3 = new Node<Integer>(3);
        Node<Integer> node4 = new Node<Integer>(4);
        Node<Integer> node5 = new Node<Integer>(10086);
        Node<Integer> node6 = new Node<Integer>(5);
        Node<Integer> node7 = new Node<Integer>(6);
        Node<Integer> node8 = new Node<Integer>(7);
        Node<Integer> node9 = new Node<Integer>(8);
        Node<Integer> node10 = new Node<Integer>(9);
        Node<Integer> node11 = new Node<Integer>(10);
        Node<Integer> node12 = new Node<Integer>(11);
        Node<Integer> node13 = new Node<Integer>(12);
        Node<Integer> node14 = new Node<Integer>(13);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        node6.next = node7;
        node7.next = node8;
        node8.next = node9;
        node9.next = node10;
        node10.next = node11;
        node11.next = node12;
        node12.next = node13;
        node13.next = node14;
        node14.next = node5;


        Node<Integer> head = node1;
        int s=20;
        while (node1 != null && (s--)>0) {
            System.out.print(node1.data + " ");
            node1 = node1.next;
        }

        if (isExistLoop(head)) {
            System.out.println("\n\n\nExist Loop of this LinkedList! \n\n\nAnd the length of the Loop is : [ "+loopLength(head)+" ] and the Linked-Node is "+findLoopEntrance(head).data);
        }

        node1 = head;
    }
}

下面我讲讲关于这个东西的数学原理。

《【Java实现数据结构】链表成环的一些检测与操作》

上面是我随手瞎几把画的一个链表示意图。各个位置都有注释标注,我就不多说了。代码的含义是取两个移动速度,一个是另外一个的两倍。当二者的位置相同时,就代表着相遇了。这时候就产生了碰撞点这个概念。

下面,是数学时间:

* 令成环部分的长度为:h
* 整条链表的长度为: l
* 在碰撞过程中,慢速的行程: w
* 头结点到连接点的距离: T
* 碰撞点到连接点的距离: P

那么,根据链表的性质: T = l - h;
而根据运动学原理: P = l - w
现在,我们只需要证明h = w 就可以得到关于连接点的获取方法中的理论支持了。
那么,根据快速与慢速的两个运动的二倍关系。有2*w - w = h。

因为很明显的快速的移动比慢速移动要多走一个环对吧?那么。。很明显,就得到了理论支撑。。。好吧。。这个是弱鸡了点,但是我想说的是,编程么。。到了最后都是数学。。语言只是工具,框架也是数学模型的合集。。不足为奇。。还是要学好数学啊!!!

至于其他的也没啥好讲的。。就那个求环长的方法,就是求w/h的长度。。那么当我们得到了碰撞点,只需要继续往前走。下一次碰撞的时候就代表着又走过了一圈。。因为他们的运动行程的两倍关系,在一个环上必然是一直在原地相遇的。。。so。。。吃饭去了。。

《【Java实现数据结构】链表成环的一些检测与操作》

正文之后

有谁想要健身方面的PDF和视频吗??

《【Java实现数据结构】链表成环的一些检测与操作》
《【Java实现数据结构】链表成环的一些检测与操作》
《【Java实现数据结构】链表成环的一些检测与操作》
《【Java实现数据结构】链表成环的一些检测与操作》

    原文作者:HustWolf
    原文地址: https://www.jianshu.com/p/41a590431865
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞