【LeetCode題解】141_環形連結串列
阿新 • • 發佈:2018-12-17
目錄
141_環形連結串列
描述
給定一個連結串列,判斷連結串列中是否有環。
進階: 你能否不使用額外空間解決此題?
解法一:雜湊表
思路
判斷一個連結串列是否包含環,可以轉化為判斷是否有一個節點之前已經出現過。非常自然的一個想法就是:遍歷連結串列的每個節點,用一個雜湊表記錄每個節點的引用(或記憶體地址);如果能夠遍歷到空節點,則此時已經遍歷到連結串列的尾部,返回 false
;如果有一個節點的引用出現在雜湊表中,則返回 true
。
Java 實現
/** * 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> nodesSeen = new HashSet<>(); while (head != null) { if (nodesSeen.contains(head)) { return true; } nodesSeen.add(head); head = head.next; } return false; } }
複雜度分析:
- 時間複雜度:\(O(n)\),其中 \(n\) 為連結串列的節點數,因為雜湊表的查詢和新增操作的時間複雜度是 \(O(1)\) 的,所以整體的時間複雜度是 \(O(n)\) 的
- 空間複雜度:\(O(n)\),最多隻需要儲存 \(n\) 個節點的引用
Python 實現
# Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def hasCycle(self, head): """ :type head: ListNode :rtype: bool """ nodes_seen = set() while head is not None: if head in nodes_seen: return True nodes_seen.add(head) head = head.next return False
複雜度分析同上。
解法二:雙指標(龜兔演算法)
思路
另一種思路就是採用 Floyd 的龜兔演算法,即借用兩個指標,一個快指標和一個慢指標,快指標每次移動兩個節點而慢指標則每次移動一個節點。如果連結串列中不存在環,則快指標會先於慢指標到達連結串列的尾部,兩個指標永遠也不可能“相遇”;相反,如果連結串列中存在環,則快指標一定會“追上”慢指標,就如同龜兔賽跑一樣,速度快的兔子一定會追上烏龜。
Java 實現
/** * 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; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return true; } } return false; } }
複雜度分析:
- 時間複雜度:\(O(n)\),其中 \(n\) 為連結串列的節點數,按照連結串列中是否存在環,分兩種情況進行討論:
- 連結串列中不存在環:快指標會優先到達連結串列尾部(最多隻需要 \(n/2\) 次),時間複雜度為 \(O(n)\)
- 連結串列中存在環:我們可以把慢指標的移動分解成兩個部分,“直線“部分和”環形“部分。對於”直線“部分,假設連結串列中”直線“部分的長度為 \(N\),則慢指標只需 \(N\) 次迭代便可到達”環形“部分,此時快指標已經進入了”環形“部分;對於”環形“部分,假設連結串列中”環形“部分的長度為 \(K\),由於兩個指標的速度之差為 1,因此最壞的情況下,經過 \(K\) 次迭代後快指標便能”追上“慢指標。綜上,最壞情況下的時間複雜度為 \(O(N + K) = O(n)\)
- 空間複雜度:\(O(1)\),只需要儲存兩個節點的引用
Python 實現
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head is None or head.next is None:
return False
slow, fast = head, head
while fast is not None and fast.next is not None:
slow, fast = slow.next, fast.next.next
if slow == fast:
return True
return False
# Runtime: 44 ms
# Your runtime beats 95.91 % of python submissions.
複雜度分析同上。