JS-單鏈表是否有環以及環的入點問題
阿新 • • 發佈:2018-12-02
給定一個連結串列,判斷連結串列中是否有環。
進階:
你能否不使用額外空間解決此題?
方案1 雜湊表
雜湊表是最容易理解的一個方案
建立一個雜湊表,如果不存在就向雜湊表中新增資料,存在的話就直接返回true(存在的可能只有P點,同時P點也是環的入點(這個和下一道題有關))
缺點:佔用大量的空間,實際中,一個連結串列中的資料是很多的,這時候你建立一個雜湊表,就會重新建一個存在大量資料的額外空間。程式碼是給電腦看的,人雖然容易理解,但是電腦不在乎容易不容易,它在乎的時間和空間的使用情況,所以這個方法不是最佳的。時間複雜度雖然是O(n),同樣空間複雜度也是O(n)
function hasCycle(head) {
let nodesSeen = new Set()
while (head != null) {
if (nodesSeen.has(head)) {
return true;
} else {
nodesSeen.add(head);
}
head = head.next;
}
return false;
}
方案2 快慢指標
建立兩個指標fast和slow
每一次迴圈fast移動兩個位置
fast=fast.next.next
slow移動一個位置
slow=slow.next
當slow==fast的時候,就說明這個連結串列中存在環,不要問為什麼?自己動手畫個圖,按著程式碼走一遍就知道,看是看不懂得,我就不說明了
優點:他的時間複雜度也是O(n)和上面的雜湊表是一樣的,但是它的空間複雜度是O(1)的,遠遠小於雜湊表的O(n),雖然但是人不容易理解,但是機器是不在乎的,你的程式碼是讓機器執行的。
var hasCycle = function(head) { if (head == null || head.next == null) { return false } let fast = head.next let slow = head while (fast != slow) { if (fast == null || fast.next == null) { return false; } fast = fast.next.next slow = slow.next } return true };
方案3 在連結串列中增加一個域visited
在連結串列中增加一個域visited(可以是a,b,c,d,…),初始化都為0,從連結串列的頭部開始走,每走過一個連結串列就標記visited為1,如果要訪問的下一個節點的visited域為1,那麼證明連結串列中有環。
上面的快慢指標和雜湊表都是需要額外的空間複雜度,進階不需要額外的空間複雜度,這裡就實現一下
var hasCycle = function(head) {
while (head) {
if (head.visited) return true
head.visited = true
head = head.next
}
return false
};
給定一個連結串列,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null。
說明:不允許修改給定的連結串列。
進階:
你是否可以不用額外空間解決此題?
方案1 雜湊表
這個就是判斷是否有環,有環的地方就是環的節點P 同樣他的缺點是需要大量的額外空間複雜度,優點就是人容易理解(沒什麼卵用,程式碼讓機器執行)
var detectCycle = function(head) {
if (head == null || head.next == null) {
return null
}
let hasObj = new Set()
while (head != null) {
if (hasObj.has(head)) {
return head
} else {
hasObj.add(head)
}
head = head.next
}
};
方案2 快慢指標
這裡前半部分和上面判斷是否有環一樣,當有環的時候,fast==slow。這時候fast返回頭結點head
fast=head
slow指標不變在原地
然後fast和slow指標每次移動一個位置,當fast==slow的時候,他們就在環的入口位置。不要問我為什麼,自己走一遍,網上的圖你看也看不懂。
fast=fast.next
slow=slow.next
優點還是那樣:只需要O(1)的額外的空間複雜度
方法3 在連結串列中增加一個域visited
道理同上面的那個方案,只是返回值不同。其實這種方法和雜湊表一樣,只是他們的是在原連結串列中添加了一個點而已
var detectCycle = function (head) {
while (head) {
if (head.visited) return head
head.visited = true
head = head.next
}
return null
};