1. 程式人生 > >JS-單鏈表是否有環以及環的入點問題

JS-單鏈表是否有環以及環的入點問題

給定一個連結串列,判斷連結串列中是否有環。

進階:

你能否不使用額外空間解決此題?

方案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

};