Java實現有環的單向連結串列,並判斷單向連結串列是否有環
有一個單向連結串列,連結串列當中有可能出現環,就像下圖這樣。我們如何判斷一個單向連結串列是否有環呢?
那麼第一步,我們先實現一個這樣的連結串列,接著再說如何判斷這樣的連結串列。
實現有環的單向連結串列
1、定義add(Node node)方法
/** * 向連結串列末尾新增結點 * * @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; }
2、先正常生成一個無環的單向連結串列,最後在末尾新增一個結點,讓該結點的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());
輸出如下:此時列印的還是一個無環單向連結串列。
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的方法我做了處理,具體細節請檢視原始碼。
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節點了,如果繼續列印,就會一直迴圈列印下去。
判斷單向連結串列是否有環
方法一:使用Hash Table
判斷一個連結串列是否有環,我們只需要判斷某個結點是否之前被訪問過即可。這裡我們優先想到的就是使用Hash表進行儲存。
我們依次遍歷訪問每個結點,並將結點的引用儲存到hash表中。如果當前結點是null,說明我們到達連結串列未尾部了,則當前連結串列一定無環;如果當前結點在hash表中已經儲存過了,此時返回true即可。程式碼如下:
/**
* 判斷單鏈表中是否有環
* 藉助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傳送門 https://github.com/tinyvampirepudge/DataStructureDemo
gitee傳送門 https://gitee.com/tinytongtong/DataStructureDemo
參考:
1、linked-list-cycle