有一個單向鏈表,鏈表當中有可能出現環,就像下圖這樣。咱們如何判斷一個單向鏈表是否有環呢?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…
參考: