1. 程式人生 > >每日一道Leetcode演算法——如何判斷一個連結串列是否有環,並求出環的入口和環的長度——

每日一道Leetcode演算法——如何判斷一個連結串列是否有環,並求出環的入口和環的長度——

判斷一個連結串列是否有環有兩種辦法:

一種最經典是定義兩個指標,一個指標每次向前走一步,一個指標每次向前走兩步,如果兩個指標最終重合。則證明有環。

一種是建立一個hash表,將每次走過的結點放入hash表中,如果結點在hash表中,則表示存在環。

判斷連結串列的入口:

假設兩個指標第一次相遇點為m,此時令一個指標從頭結點向下走,每次走一步,令另一個指標從相遇點往下走,每次走一步,兩個指標相遇的位置,為入口處。

判斷環的長度:

設定一個指標q指向環的入口,讓q往後移動直到q再次等於環的入口結點,此時q所走的步數就是環的長度。

package cn.leetcode.linkedlist;

import cn.kimtian.linkedlist.Node;

import java.util.HashMap;

/**
 * 1.如何判斷一個連結串列是否有環
 * 2.如果有環,找到環入口
 * 3.如果有環,求環的長度
 *
 * @author kimtian
 */
public class LinkLoop {
    public static void main(String[] args) {
        //建立一個連結串列,為A->B->C->D->E->B
        Node nA = new Node(1);
        Node nB = new Node(2);
        Node nC = new Node(3);
        Node nD = new Node(4);
        Node nE = new Node(5);
        nA.next = nB;
        nB.next = nC;
        nC.next = nD;
        nD.next = nE;
        //這句使其成為一個帶環的連結串列
        nE.next = nB;
        System.out.println(hasLoop(nA));
        System.out.println(searchEntranceNode(nA).getData());
        System.out.println(circleLength(nA));
    }

    /**
     * 方法一:建立兩個指標,一個走一步,一個走兩步,如果兩個指標最後重合,說明這個連結串列有環
     *
     * @param n 連結串列頭結點
     * @return 是否有環
     */
    public static boolean hasLoop(Node n) {
        //單鏈表為空時,單鏈表沒有環
        if (n == null) {
            return false;
        }
        //單鏈表中只有頭結點,而且頭結點的next為空,單鏈表沒有環
        if (n.next == null) {
            return false;
        }
        //定義兩個指標tmp1,tmp2,一個走一步,一個走兩部
        Node tmp1 = n.next;
        Node tmp2 = n.next.next;

        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                //如果兩個指標最後重逢,說明存在環,否則不存在
                return true;
            }
            //每次迭代指標一走一步,指標二走兩步
            tmp1 = tmp1.next;
            //如果下一個都為空了,就不要找下下一個的值了,防止空指標異常
            if (tmp2.next != null) {
                tmp2 = tmp2.next.next;
            }
            //沒有下一個指向了,說明不是環
            else {
                return false;
            }
        }
        return false;
    }

    /**
     * 方法二:將每次走過的結點儲存到hash表中,如果結點在hash表中,則表示存在環
     *
     * @param n 連結串列頭結點
     * @return 是否有環
     */
    public static boolean hasLoop2(Node n) {
        Node temp1 = n;
        HashMap<Node, Node> ns = new HashMap<Node, Node>();
        while (n != null) {
            if (ns.get(temp1) != null) {
                return true;
            } else {
                ns.put(temp1, temp1);
            }
            temp1 = temp1.next;
            if (temp1 == null) {
                return false;
            }
        }
        return true;
    }

    /**
     * 如果有環,求環的入口結點
     *
     * @param n 連結串列頭結點
     * @return 入口結點
     */
    public static Node searchEntranceNode(Node n) {
        //單鏈表為空時,單鏈表沒有環
        if (n == null) {
            return null;
        }
        //單鏈表中只有頭結點,而且頭結點的next為空,單鏈表沒有環
        if (n.next == null) {
            return null;
        }
        //定義兩個指標tmp1,tmp2,一個走一步,一個走兩部
        Node tmp1 = n.next;
        Node tmp2 = n.next.next;

        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                //如果兩個指標最後重逢,說明存在環,否則不存在
                break;
            }
            //每次迭代指標一走一步,指標二走兩步
            tmp1 = tmp1.next;
            //如果下一個都為空了,就不要找下下一個的值了,防止空指標異常
            if (tmp2.next != null) {
                tmp2 = tmp2.next.next;
            }
            //沒有下一個指向了,說明不是環
            else {
                return null;
            }
        }
        //這裡直接等於頭結點,沒有向下走一步,因為頭結點有可能就是環的入口結點
        tmp2 = n;
        //假設兩個指標第一次相遇點為m,此時令一個指標從頭結點向下走,每次走一步,令另一個指標從相遇點往下走,每次走一步,兩個指標相遇的位置,為入口處
        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                return tmp1;
            }
            tmp1 = tmp1.next;
            tmp2 = tmp2.next;
        }

        return null;
    }

    /**
     * 如果有環,求環的長度
     * 設定一個指標q指向環的入口,讓q往後移動直到q再次等於環的入口結點,此時q所走的步數就是環的長度
     *
     * @param n 連結串列頭結點
     * @return 入口結點
     */
    public static int circleLength(Node n) {
        Node p = searchEntranceNode(n);
        //不存在環時,返回0
        if (p == null) {
            return 0;
        }
        Node q = p.next;
        int length = 1;
        while (p != q) {
            length++;
            q = q.next;
        }
        //返回環的長度
        return length;
    }
}