Java單鏈表基本操作(十)--判斷單鏈表是否有環並輸出環長度以及環的入口節點

本文解決三個問題:

1.單鏈表是否有環?
2.有則輸出環的長度?
3.找到環的入口節點?

分析:
定義兩個指針fast 和slow,fast每次向後移動兩個節點,slow每次想後移動一個節點。
1.如果沒有環,則fast首先到達鏈表結尾;
2.鏈表有環的情況下:fast與slow兩次相遇,slow中間走過的節點處即爲環的長度;
3.找環的入口節點稍微複雜點,有如下的推導過程:

相遇的時候,slow共移動了s步,fast共移動了2s步。
定義a如下: 鏈表頭移動a步到達入口點。
定義x如下: 入口點移動x步到達相遇點。
定義r如下: 環的長度。
定義L如下: 鏈表總長度爲L。

其中L = a + r

那麼slow和fast相遇了,fast必然比slow多走了n個圈,也就是 n*r 步,那麼
s = a + x
2s = s + n*r , 可得 s = n*r
將s=a+x,帶入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r   
從表頭移動到入口點,再從入口點移動到相遇點,也就是移動了整個鏈表的距離,即是 L = a + r , 所以r = L - a
所以 a+x = (n-1)*r + L - a , 於是 a = (n-1)*r + L - a - x
得到:從表頭到入口點的距離,等於從相遇點到入口點的距離
所以,從表頭設立一個指針,從相遇點設立一個指針,兩個同時移動,必然能夠在入口點相遇,這樣,就求出了相遇點。

上面三個問題的java解決代碼:

方法一:一次性求解

public class ExistCircle {
    static int id = 1;
    public Node existCircle(Node head){
        Node fast = head;
        Node slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            //輸出環長
            while(fast == slow) {
                int len = 1;
                fast = fast.next.next;
                slow = slow.next;
                while (fast != slow) {
                    len++;
                    fast = fast.next.next;
                    slow = slow.next;
                }
                System.out.println("The length of circle is:" + len);

                //輸出環入口節點
                fast = head;
                while (fast != slow) {
                    fast = fast.next;
                    slow = slow.next;
                    id++;
                }
                return slow;
            }
        }
        return null;
    }
    public static void main(String[] args) {
        Node head=new Node(3);
        Node node1=new Node(6);
        Node node2=new Node(8);
        Node node3=new Node(5);
        Node node4=new Node(2);
        Node node5=new Node(7);
        head.next=node1;
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node3;
        Node port = new ExistCircle().existCircle(head);
        System.out.println("環入口爲第" + id + "個節點:" + port.data);
    }
}

方法二:分開求解:

package listnode;
/**
 * @author Gavenyeah
 * @date Time: 2016年5月18日上午11:36:40
 * @des: 
 */
public class ExitCircle {
    static int id = 1;

    public static void main(String[] args) {
        //測試
        Node head=new Node(3);
        Node node1=new Node(6);
        Node node2=new Node(8);
        Node node3=new Node(5);
        Node node4=new Node(2);
        Node node5=new Node(7);
        head.next=node1;
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node3;
        new ExitCircle().exitCircle(head);

        Node port = new ExitCircle().findLoopPort(head);
        System.out.println("環入口爲第" + id + "個節點:" + port.data);

    }
    //環入口節點
    //環的入口節點到快慢指針相遇的距離 與 鏈表頭節點到環入口節點的距離相等
    public Node findLoopPort(Node head){
        Node slow = head, fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                fast = head;
                while(head != slow){
                    id++;
                    head = head.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        System.out.print("NoLoop !");
        return null;
    }

    public boolean exitCircle(Node head){
        Node fast = head;
        Node slow = head;
        while(fast != null && fast.next != null){//判斷是否由環,注意fast.next = null的情況
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){//存在環
                int count = 0;
                while(true){
                    count ++;
                    fast = fast.next.next;
                    slow = slow.next;

                    if(fast == slow){//快慢指針在第二次相遇,這個點肯定是第一次相遇的點 
                        System.out.println("環的長度:" + count);
                        return true;
                    }
                }
            }
        }
        System.out.println("false!");
        return false;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章