題解:環形連結串列
在這道題目中,我們需要判斷一個連結串列是否存在環。環的定義是連結串列的某個節點可以透過連續跟蹤 next
指標回到自身。如果存在這樣的環,那麼就返回 true
,否則返回 false
。
方法一:使用雜湊集合 (HashSet)
思路:
- 遍歷連結串列,使用一個雜湊集合 (
HashSet
) 儲存每個訪問過的節點。 - 每遍歷一個節點時,檢查該節點是否已經存在於集合中。如果存在,說明連結串列中存在環;如果不存在,則將該節點加入集合中。
- 如果遍歷完整個連結串列都沒有發現重複節點,說明連結串列中不存在環。
程式碼實現:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
ListNode curr = head;
while (curr != null) {
// 如果當前節點已經在集合中,說明有環
if (!set.add(curr)) {
return true;
}
curr = curr.next; // 繼續遍歷下一個節點
}
return false; // 如果遍歷到連結串列末尾沒有發現環
}
}
複雜度分析:
- 時間複雜度:O(n),其中
n
是連結串列的節點數。我們每個節點只遍歷一次。 - 空間複雜度:O(n),需要額外的雜湊集合來儲存每個訪問的節點。
過程解析:
- 初始化一個空的
HashSet
。 - 從頭節點開始遍歷連結串列,每遇到一個節點,檢查它是否在
HashSet
中。如果在,說明有環,返回true
;否則,將該節點加入集合,繼續遍歷下一個節點。 - 如果遍歷到了連結串列的末尾(即
curr == null
),說明連結串列沒有環,返回false
。
方法二:快慢指標 (Floyd 判圈演算法)
思路:
- 使用兩個指標:一個快指標 (
fast
),一個慢指標 (slow
)。 - 快指標每次走兩步,慢指標每次走一步。
- 如果連結串列中沒有環,快指標會在遍歷完連結串列時到達
null
。 - 如果連結串列中有環,快指標會最終追上慢指標,兩者會相遇。
程式碼實現:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head; // 慢指標
ListNode fast = head.next; // 快指標
// 快慢指標相遇說明有環
while (fast != slow) {
if (fast == null || fast.next == null) return false; // 快指標提前到達終點,說明沒有環
fast = fast.next.next; // 快指標走兩步
slow = slow.next; // 慢指標走一步
}
return true; // 兩個指標相遇,說明有環
}
}
複雜度分析:
- 時間複雜度:O(n),其中
n
是連結串列的節點數。最壞情況下,快指標和慢指標遍歷連結串列中的每個節點。 - 空間複雜度:O(1),我們只用了常數級別的額外空間。
過程解析:
- 快指標
fast
每次走兩步,慢指標slow
每次走一步。 - 如果連結串列中有環,快指標最終會追上慢指標,這時返回
true
。 - 如果快指標在遍歷過程中到達了
null
,則連結串列沒有環,返回false
。
方法比較:
- 雜湊集合法使用了額外的儲存空間,但思路簡單易懂,適合初學者。
- 快慢指標法雖然稍微複雜一點,但它不需要額外的空間,能夠以 O(1) 的空間複雜度解決問題,在面試中常被要求使用這種方法。
這道題目考察了連結串列的基本操作以及快慢指標的應用,透過分析連結串列的結構,可以更好地掌握連結串列與指標的使用。