目錄
描述
給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null
。
說明:不允許修改給定的鏈表。
進階:
你是否可以不用額外空間解決此題?
解法一:哈希表
思路
最直接的解法就是利用一個集合保存每次遍歷的節點的引用。之後,從鏈表頭開始遍歷,每遍歷一個節點,就判斷該節點的引用是否在集合中,如果不在集合中,則將該節點的引用放入集合中;如果在集合中,則返回該節點的引用(環的入口)。當然,如果能遍歷到鏈表尾部,此時鏈表無環,返回 null
。
Java 實現
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
import java.util.Set;
import java.util.HashSet;
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode curr = head;
Set<ListNode> nodesSeen = new HashSet<>();
while (curr != null) {
if (nodesSeen.contains(curr)) {
return curr;
}
nodesSeen.add(curr);
curr = curr.next;
}
return curr;
}
}
Python 實現
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
curr = head
nodes_seen = set()
while curr:
if curr in nodes_seen:
return curr
nodes_seen.add(curr)
curr = curr.next
return curr
複雜度分析
- 時間複雜度:\(O(n)\)
- 空間複雜度:\(O(n)\)
解法二:雙指針
思路
和 LeetCode 第 141 題一樣,如果不想佔用額外的空間的話,可以採用雙指針的方式。
假設鏈表的起始節點爲 A,環的入口節點爲 B,兩個指針(快慢指針)相交節點爲 C,AB 兩點之間的長度爲 \(x\),BC 兩點之間的長度爲 \(y\),CB 兩點之間的長度爲 \(z\)。慢指針 slow
走過的長度爲 \(x+y\),快指針 fast
爲了“趕上”慢指針,應該走過的長度爲 \(x + y + z + y\),同時,由於快指針的速度是慢指針的兩倍,因此相同時間內,快指針走過的路程應該是慢指針(走過的路程)的兩倍,即
\[
x + y + z + y = 2 (x + y)
\]
化簡得,
\[
x = z
\]
因此,如果此時有另外一個慢指針 slow2
從起始節點 A 出發,則兩個慢指針會在節點 B (環的入口)相遇。
Java 實現
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
ListNode slow2 = head;
while (slow != slow2) {
slow = slow.next;
slow2 = slow2.next;
}
return slow;
}
}
return null;
}
}
// Runtime: 1 ms
// Your runtime beats 100.00 % of python submissions.
Python 實現
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
slow2 = head
while slow != slow2:
slow = slow.next
slow2 = slow2.next
return slow
return None
# Runtime: 44 ms
# Your runtime beats 99.73 % of python submissions.
複雜度分析
- 時間複雜度:\(O(n)\),其中 \(n\) 表示鏈表的長度。最壞的情況下(鏈表有環),需要迭代的次數爲 \(x + y + z = n\) 次,因此時間複雜度爲 \(O(n)\)
- 空間複雜度:\(O(1)\),只需要存儲 3 個引用