一、題目
給定一個鏈表,判斷鏈表中是否有環。
爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。
示例3:
輸入:head = [1], pos = -1
輸出:false
解釋:鏈表中沒有環。
進階:你能用 O(1)(即,常量)內存解決此問題嗎?
二、題解
- 解法1:哈希表
遍歷所有節點並把每個節點的引用存儲到哈希表中。如果當前節點的引用已經存在於哈希表中,說明該鏈表是環形鏈表,返回 true;反之,則遍歷到尾,即該節點的下一個節點爲 null,該鏈表不是環形鏈表,返回 false。
時間複雜度:O(n),空間複雜度:O(n)。
function hasCycle($head) {
$hashMap = [];
$curr = $head;
while ($curr != null && $curr->next != null) {
if (in_array($curr, $hashMap)) {
return true;
}
$hashMap[] = $curr;
$curr = $curr->next;
}
return false;
}
- 解法2:雙指針——快慢指針
快指針:一次兩步
慢指針:一次一步
假設一個環形賽道,兩個運動員 A 和 B,A 領先 B 一步:
在一個圓裏,速度快的 A 在跑了 n 圈後,一定能遇到速度慢的 B
——對應鏈表,就是兩個指針重合
如果不是圓,速度快的 A 一定先到達終點,則說明不存在環
——對應鏈表,就是結尾指針指向null
時間複雜度:O(n),空間複雜度:O(1)。
function hasCycle($head) {
if ($head == null || $head->next == null) {
return false;
}
//快慢指針
$slow = $head;
$fast = $head->next;
while ($slow != $fast) {
//沒有環,fast走到鏈表尾部,fast爲空或者fast的next爲空
if ($fast == null || $fast->next == null) {
return false;
}
$slow = $slow->next;
$fast = $fast->next->next;
}
return true;
}
快慢指針法是看了題解後做出來的,一開始有點不明白:爲什麼快指針速度爲 2,慢指針速度爲 1?快指針速度不能爲 3 甚至更大嗎?
畫圖畫了幾遍,大概理解了:
fast 走 2 步,slow走 1 步,如果有環,則 fast 會碰到 slow 或者 fast 會走到 slow 的後面。
如果是 fast 走到 slow 後面,這時候兩個指針每走一次它們之間的距離就會縮小 1,最後無論多遠兩個指針肯定會相遇。
但是如果是 fast 走 3 步,slow 走 1 步,同樣是前面的情況,它們之間的距離每次縮小 2,最後 fast 又跑到 slow 前面去了,所以如果 fast 設置爲 3,需要多加一個判斷條件,見下面代碼 while
處。
function hasCycle($head) {
if ($head == null || $head->next == null) {
return false;
}
//快慢指針
$slow = $head;
$fast = $head->next->next;
while ($slow != $fast || $slow->next != $fast) {
//沒有環,fast走到鏈表尾部,fast爲空或者fast的next爲空
if ($fast == null || $fast->next == null) {
return false;
}
$slow = $slow->next;
$fast = $fast->next->next->next;
}
return true;
}