用 龜兔賽跑演算法 來檢查有沒有環路Linked List Cycle_Leetcode #141

閱讀時間約 5 分鐘

題目敘述

給定一個Linked list鏈結串列的Head node,

請判斷這條Linked list是否存在環路(Cycle)?

如果有環路,回傳True。

如果沒有,回傳False。


題目的原文敘述


測試範例

Example 1:

raw-image


Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).
有環路Cycle​


Example 2:

raw-image


Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.
有環路Cycle


Example 3:

raw-image


Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
沒有環路 

約束條件

Constraints:

  • The number of the nodes in the list is in the range [0, 10^4].

Linked list的節點總數目介於 0 ~ 一萬之間。

  • -10^5 <= Node.val <= 10^5

每個節點的節點值都介於 負十萬~正十萬 之間。

  • pos is -1 or a valid index in the linked-list.

pos代表發生環路的節點編號,如果是-1代表沒有環路。


進階提問

Follow up: Can you solve it using O(1) (i.e. constant) memory?

能不能在常數空間複雜度的條件下,解出這題?


演算法 雙指針(快/慢 指針) 俗稱龜兔賽跑演算法

有一個比較直覺的想法,就是沿路記下每個節點的ID,在C/C++裡面可以記錄節點的記憶體位置,在python裡面可以記錄run-time的物件ID。如果在拜訪的過程中,遇到相同的ID,就代表有環路。

但是,這個方法會耗費O(n)的空間成本去紀錄節點的ID,有沒有更好的方法呢?

其實有喔!


(俗稱龜兔賽跑演算法,或者Floyd cycle detection algorithm)

想一下,假如有環路發生,我們可以說linked list中的某一塊形成了封閉的環型,就好像操場跑道的形狀

我們讓兩個跑者(在演算法裡面就是雙指針,快/慢指針),同時從起點出發,快的固定都跑得比慢的還快,最後快的一定可以領先一圈,又遇到慢的跑者




相反的,如果沒有環路,相當於linked list是一條直線型的跑道,那麼我們讓兩個跑者同時從起點出發,兩者並不會在中途相遇


為了方便實作,我們可以假設快跑者是慢跑者的兩倍速度,讓他們同時從起點出發

假如快、慢跑者中途會相遇,那麼代表linked list 存在至少一個環路
假如快、慢跑者不會相遇,那麼代表linked list是一條直線的串列,沒有環路

這樣我們只要維護兩個pointer 快、慢指針就相當於快、慢跑者,
只需要常數空間O(1)即可。


程式碼 雙指針(快/慢 指針) 俗稱龜兔賽跑演算法

class Solution:
def hasCycle(self, head: ListNode) -> bool:

# two pointers
slow, fast = head, head

# slow runner move 1 step, fast runner move 2 steps per iteration
# if fast runner meets slow one somewhere, then there is a cycle in linked list

while fast and fast.next:

slow, fast = slow.next, fast.next.next

if slow is fast:
return True

return False

複雜度分析

時間複雜度:

需要掃描過每個節點,所需時間為O(n)。

空間複雜度:

只需要維護固定尺寸的臨時變數,所需空間為O(1) 常數級別。


關鍵知識點

假如有環路發生,我們可以說linked list中的某一塊形成了封閉的環型,就好像操場跑道的形狀

我們讓兩個跑者(在演算法裡面就是雙指針,快/慢指針),同時從起點出發,快的固定都跑得比慢的還快,最後快的一定可以領先一圈,又遇到慢的跑者


相反的,如果沒有環路,相當於linked list是一條直線型的跑道,那麼我們讓兩個跑者同時從起點出發,兩者並不會在中途相遇


Reference:

[1] Linked List Cycle - LeetCode

85會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
發表第一個留言支持創作者!
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
題目敘述 題目會給我們一個鏈結串列的起點head,要求我們找出這個串列的中點。 註: 如果串列長度是偶數,就回傳中間偏右的那個節點。 例如: 1 -> 2 -> 3 回傳中點為2 1 -> 2 -> 3 -> 4 ->5 -> 6 回傳中點為4 詳細的題目可在這裡看到 測試範例
題目會給定一個帶有Random Pointer的鏈結串列,要求我們實體複製deep copy這條鏈結串列,並且輸出副本的根結點。
題目會給定我們一個串列,和一個n值,要求我們刪除尾巴數來的第n個節點。 例如 1->2->3->4->5 和 給定n值=2,要求我們刪除尾巴數來的第2個節點。 尾巴數來的第2個節點是4,刪除之後,更新連結,輸出答案如下 1->2->3->5
題目會給我們兩條已經從小到大排序好的串列,要求我們依照從小到大的順序,合併這兩條串列。
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
題目敘述 題目會給我們一個鏈結串列的起點head,要求我們找出這個串列的中點。 註: 如果串列長度是偶數,就回傳中間偏右的那個節點。 例如: 1 -> 2 -> 3 回傳中點為2 1 -> 2 -> 3 -> 4 ->5 -> 6 回傳中點為4 詳細的題目可在這裡看到 測試範例
題目會給定一個帶有Random Pointer的鏈結串列,要求我們實體複製deep copy這條鏈結串列,並且輸出副本的根結點。
題目會給定我們一個串列,和一個n值,要求我們刪除尾巴數來的第n個節點。 例如 1->2->3->4->5 和 給定n值=2,要求我們刪除尾巴數來的第2個節點。 尾巴數來的第2個節點是4,刪除之後,更新連結,輸出答案如下 1->2->3->5
題目會給我們兩條已經從小到大排序好的串列,要求我們依照從小到大的順序,合併這兩條串列。
你可能也想看
Google News 追蹤
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
在短影音盛行的年代,龜兔賽跑帶給我們的啟示依然適用嗎? 我認為是的,而且更加適用,細節後面會說到,在那之前,我想先說說我的發現。 我發現,當整個世界都在搶快,真正厲害的人, 反而都在追求「慢」或者說「不急」。
Thumbnail
固定型思維者多半會將單件事物的成敗連結到自我天賦能力上,如挫敗了就會歸因於就是自己能力差,就是無法做好。 成長型思維者不會因為單次的挫敗灰心喪志,會認為那是目前單次事件的失敗,並會在下次持續累積成長並做得更好。具體來說會針對事情的檢討而不針對自我能力懷疑沮喪。
對於很多人來說,打工遊學是一種探索世界的方式,同時也是為未來投資的一種方式。如果你正在考慮打工遊學,特別是在愛爾蘭這個充滿魅力的國家,那麼理解愛爾蘭打工遊學的費用非常重要。
Thumbnail
之前,我討論了「敏捷需求」的概念,這個概念被埋入 Nokia 測試(Nokia Test)中。敏捷需求沒有被普遍認同的定義,所以我現在用一個更好說明的標準概念來談。對於許多應用程式來講,特別是網路應用程式,一個故事(Story)只需要...
Thumbnail
泡澡是人們日常生活中的一種舒適放鬆的方式。而選擇一個合適的浴缸,不僅可以增加浴室的舒適度和美觀度,更可以讓泡澡的體驗更加愉悅。然而,在市場上浴缸的種類繁多,符合需求的不見得是你期待的,這時就會需要依賴專業服務了,一起來看看如何規劃衛浴空間的泡澡區,以及有哪些需要注意的小地方喔!! 浴缸有哪些類型 獨
Thumbnail
下筆之前的構思過程非常重要,他考驗的是學生的思辨能力。周全的構思雖然會多花幾分鐘時間,但不僅可以避免寫太快而爛尾收場,也可以精準找到最適切的題材,不至於叫苦連天寫不出來。今天來介紹一個構思的方法,它可以幫助學生在眾多題材當中選出一個最恰當的,那就是--歸納圖。
Thumbnail
在龜兔賽跑的故事裡,一般都會認為是烏龜很勤奮,才能贏得最後的成功,但真的只需要努力就能成功嗎? 這裡來談談兩個重要的因素,運氣、在失敗中堅持下去。 可以老實的說,烏龜的運氣非常好,遇到一個傲慢、自大的兔子。假如烏龜遇到是一隻正常的兔子,這個寓言故事就不會被廣為流傳。 運氣很重要 在失敗中堅持
【不要害怕】 可能最近一直貼疫情的話題,讓大家緊張 但是我不是要傳遞這種恐懼 只是希望大家不要輕敵,要警醒,不是驚醒 龜兔賽跑不就是兔子輸了 這個輸了只不過是一個故事而已 我們輸了可能是好幾條生命 或是一輩子的後遺症, 希望大家是可以多多分享防疫妙招 為彼此的健康,為疫情祈禱, 也沒看到這樣的消息
Thumbnail
線條,夜幕低垂趁著些微暮光,華燈漸上,我抬頭望著這座吊具,輕喟這具幾何鐵架構成的,挺立在半空中的線條美感。 忘不了你輕輕搖頭,或輕或重的道出:「因為你輸在起跑點了。」這樣的事實。語畢空氣間一片沉默,我預期杌隉不安的情緒並沒有出現,反倒坦然的笑得一塌糊塗。興許是我心有所感,甚懂你的辭不達意。 我並不
Thumbnail
在愛情裡沒有誰輸誰贏,贏也得是雙贏,輸了肯定是兩敗俱傷。《2046》說過:「愛情講究的是時機,時機是很難拿捏的」。不斷錯過表白時機的烏龜,要怎麼讓兔子愛上她呢?陳導深知王家衛式「求敗」之美,畢竟,在「愛,無能」時,我們總是覺得自己慢了一點,又或者快了一點,時機是很難拿捏的。
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
在短影音盛行的年代,龜兔賽跑帶給我們的啟示依然適用嗎? 我認為是的,而且更加適用,細節後面會說到,在那之前,我想先說說我的發現。 我發現,當整個世界都在搶快,真正厲害的人, 反而都在追求「慢」或者說「不急」。
Thumbnail
固定型思維者多半會將單件事物的成敗連結到自我天賦能力上,如挫敗了就會歸因於就是自己能力差,就是無法做好。 成長型思維者不會因為單次的挫敗灰心喪志,會認為那是目前單次事件的失敗,並會在下次持續累積成長並做得更好。具體來說會針對事情的檢討而不針對自我能力懷疑沮喪。
對於很多人來說,打工遊學是一種探索世界的方式,同時也是為未來投資的一種方式。如果你正在考慮打工遊學,特別是在愛爾蘭這個充滿魅力的國家,那麼理解愛爾蘭打工遊學的費用非常重要。
Thumbnail
之前,我討論了「敏捷需求」的概念,這個概念被埋入 Nokia 測試(Nokia Test)中。敏捷需求沒有被普遍認同的定義,所以我現在用一個更好說明的標準概念來談。對於許多應用程式來講,特別是網路應用程式,一個故事(Story)只需要...
Thumbnail
泡澡是人們日常生活中的一種舒適放鬆的方式。而選擇一個合適的浴缸,不僅可以增加浴室的舒適度和美觀度,更可以讓泡澡的體驗更加愉悅。然而,在市場上浴缸的種類繁多,符合需求的不見得是你期待的,這時就會需要依賴專業服務了,一起來看看如何規劃衛浴空間的泡澡區,以及有哪些需要注意的小地方喔!! 浴缸有哪些類型 獨
Thumbnail
下筆之前的構思過程非常重要,他考驗的是學生的思辨能力。周全的構思雖然會多花幾分鐘時間,但不僅可以避免寫太快而爛尾收場,也可以精準找到最適切的題材,不至於叫苦連天寫不出來。今天來介紹一個構思的方法,它可以幫助學生在眾多題材當中選出一個最恰當的,那就是--歸納圖。
Thumbnail
在龜兔賽跑的故事裡,一般都會認為是烏龜很勤奮,才能贏得最後的成功,但真的只需要努力就能成功嗎? 這裡來談談兩個重要的因素,運氣、在失敗中堅持下去。 可以老實的說,烏龜的運氣非常好,遇到一個傲慢、自大的兔子。假如烏龜遇到是一隻正常的兔子,這個寓言故事就不會被廣為流傳。 運氣很重要 在失敗中堅持
【不要害怕】 可能最近一直貼疫情的話題,讓大家緊張 但是我不是要傳遞這種恐懼 只是希望大家不要輕敵,要警醒,不是驚醒 龜兔賽跑不就是兔子輸了 這個輸了只不過是一個故事而已 我們輸了可能是好幾條生命 或是一輩子的後遺症, 希望大家是可以多多分享防疫妙招 為彼此的健康,為疫情祈禱, 也沒看到這樣的消息
Thumbnail
線條,夜幕低垂趁著些微暮光,華燈漸上,我抬頭望著這座吊具,輕喟這具幾何鐵架構成的,挺立在半空中的線條美感。 忘不了你輕輕搖頭,或輕或重的道出:「因為你輸在起跑點了。」這樣的事實。語畢空氣間一片沉默,我預期杌隉不安的情緒並沒有出現,反倒坦然的笑得一塌糊塗。興許是我心有所感,甚懂你的辭不達意。 我並不
Thumbnail
在愛情裡沒有誰輸誰贏,贏也得是雙贏,輸了肯定是兩敗俱傷。《2046》說過:「愛情講究的是時機,時機是很難拿捏的」。不斷錯過表白時機的烏龜,要怎麼讓兔子愛上她呢?陳導深知王家衛式「求敗」之美,畢竟,在「愛,無能」時,我們總是覺得自己慢了一點,又或者快了一點,時機是很難拿捏的。