Java實現有環的單向鏈表,並判斷單向鏈表是否有環

Java實現有環的單向鏈表,並判斷單向鏈表是否有環

有一個單向鏈表,鏈表當中有可能出現環,就像下圖這樣。咱們如何判斷一個單向鏈表是否有環呢?node

有環的單向鏈表

那麼第一步,咱們先實現一個這樣的鏈表,接着再說如何判斷這樣的鏈表。git

實現有環的單向鏈表

一、定義add(Node node)方法github

/**
     * 向鏈表末尾添加結點
     *
     * @param node 結點的next指向爲null,表示尾結點。
     * @return
     */
    public boolean add(SingleNode node) {
        if (first == null) {
            first = node;
        } else {
            SingleNode last = get(getSize() - 1);
            last.next = node;
        }
        size++;
        return true;
    }

    /**
     * 末尾添加一個特殊結點,造成一個環,爲了測試用。
     *
     * @param node 這個node的next指向單項鍊表中已經存在的結點。
     * @return
     */
    public boolean addCycleNode(SingleNode node) {
        if (getSize() < 2) {
            return false;
        }
        SingleNode last = get(getSize() - 1);
        last.next = node;
        return true;
    }
複製代碼

二、先正常生成一個無環的單向鏈表,最後在末尾添加一個結點,讓該結點的next指向前面的某個結點。算法

// 循環鏈表
        SingleLinkedList sll = new SingleLinkedList();
        SingleNode head = new SingleNode(0, null);
        sll.add(head);
        for (int i = 1; i < 10; i++) {
            SingleNode node = new SingleNode(i, null);
            sll.add(node);
        }
        SingleNode sn = new SingleNode(111, null);
        sll.add(sn);
        for (int i = 10; i < 20; i++) {
            SingleNode node = new SingleNode(i, null);
            sll.add(node);
        }
        System.out.println(sll.toString());
複製代碼

輸出以下:此時打印的仍是一個無環單向鏈表。bash

SingleLinkedList:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 111, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
複製代碼

接着添加最後一個結點,讓其next指向以前的sn結點,這樣就造成了一個閉環。測試

// 最後一個結點的next指向以前添加的某個結點。
        SingleNode last = new SingleNode(222, sn);
        sll.add(last);

複製代碼

接着打印結果:須要注意的是,這裏的打印log的方法我作了處理,具體細節請查看源碼。ui

cycle:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 111, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 222, 111]
複製代碼

最後插入的結點的值爲222,它的next指向的是111結點,如上所示,在222以後打印的就是111節點了,若是繼續打印,就會一直循環打印下去。spa

判斷單向鏈表是否有環

方法一:使用Hash Table 判斷一個鏈表是否有環,咱們只須要判斷某個結點是否以前被訪問過便可。這裏咱們優先想到的就是使用Hash表進行存儲。 咱們依次遍歷訪問每一個結點,並將結點的引用存儲到hash表中。若是當前結點是null,說明咱們到達鏈表未尾部了,則當前鏈表必定無環;若是當前結點在hash表中已經存儲過了,此時返回true便可。代碼以下:.net

/**
     * 判斷單鏈表中是否有環
     * 藉助HashSet來判斷。
     *
     * @param head
     * @return
     */
    public boolean hasCycleByHashSet(SingleNode head) {
        Set<SingleNode> set = new HashSet<>();
        SingleNode node = head;
        while (node != null) {
            if (set.contains(node)) {
                return true;
            } else {
                set.add(node);
            }
            node = node.next;
        }
        return false;
    }
複製代碼

方法二:使用快慢指針 首先建立兩個指針1和2(在Java裏就是兩個對象引用),同時指向這個鏈表的頭結點。而後開始一個大循環,在循環體中,1每次移動一個結點,2每次移動2個結點。最後比較這兩個結點指向的結點是否相同。若是相同,則判斷出鏈表有環,若是不一樣,則繼續下一次循環。指針

代碼以下:

/**
     * 判斷單鏈表中是否有環
     * 藉助快慢指針來判斷。
     *
     * @param head
     * @return
     */
    public boolean hasCycleByTowPointers(SingleNode head) {
        // 排除無數據或者只有一個數據且無閉環的狀況
        if (head == null || head.next == null){
            return false;
        }
        SingleNode slow =  head;
        SingleNode fast = head.next;
        while(slow != fast){
            // 判斷快指針結點是否爲null,若是爲null,則說明到達單向鏈表的結尾了。
            if(fast == null || fast.next == null){
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
複製代碼

完整代碼請查看

項目中搜索SingleLinkedList便可。

github傳送門 github.com/tinyvampire…

gitee傳送門 gitee.com/tinytongton…

參考:

一、linked-list-cycle

二、漫畫算法:如何判斷鏈表有環?

三、使用Java實現單向鏈表,並完成鏈表反轉。

相關文章
相關標籤/搜索