問題描述
給定一個鏈表,判斷鏈表中是否有環。
為了表示給定鏈表中的環,我們使用整數 pos
來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos
是 -1
,則在該鏈表中沒有環。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。
進階:
你能用 O(1)(即,常量)內存解決此問題嗎?
解決方案
快慢指針法
想象一下,有兩個速度不同的跑步者。如果他們在直路上行駛,快跑者將首先到達目的地。但是,如果它們在圓形跑道上跑步,那么快跑者如果繼續跑步就會追上慢跑者。
這正是我們在鏈表中使用兩個速度不同的指針時會遇到的情況:
如果沒有環,快指針將停在鏈表的末尾。
如果有環,快指針最終將與慢指針相遇。
所以剩下的問題是:
這兩個指針的適當速度應該是多少?
一個安全的選擇是每次移動慢指針一步
,而移動快指針兩步
。每一次迭代,快速指針將額外移動一步。如果環的長度為 M,經過 M 次迭代后,快指針肯定會多繞環一周,並趕上慢指針。
時間復雜度:O(n)
空間復雜度:O(1)
show me the code
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
quick_pointer = slow_pointer = head # 創建兩個指針,分別記錄一個快的遍歷指針,和一個慢的便利指針
while quick_pointer and quick_pointer.next: # 當快指針為None,或快指針的下個一對象為None,說明不是環形鏈表
slow_pointer = slow_pointer.next # 慢指針前進一步
quick_pointer = quick_pointer.next.next # 快指針前進兩步
if slow_pointer == quick_pointer: # 假如快指針等於慢指針,說明為環形鏈表
return True
return False
hash表存貯法
順序遍歷鏈表所有節點,若出現重復訪問則說明有環,否則說明無環。這里注意不能用list保存訪問過的節點,查找太慢了;用dict保存還要考慮到鍵不能是對象,所以這里采取以對象的id作為鍵的做法。
時間復雜度:O(n)
空間復雜度:O(n)
show me the code
class Solution2(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
my_dict = {} # 創建一個字典來保存已經遍歷過節點的內存地址
while head and head.next: # 當快指針為None,或快指針的下個一對象為None,說明不是環形鏈表
if id(head) in my_dict: # 假如當前節點在字典中則說明是環形鏈表
return True
else:
my_dict[id(head)] = True # 將當前節點加入到字典中
return False
逆轉鏈表檢測法
倘若一個鏈表存在環,那么將這個鏈表反轉,反轉后的鏈表和原鏈表具有相同的head。證明起來比較麻煩,可以在紙上畫一畫來驗證。
時間復雜度:O(n)
空間復雜度:O(n)
show me the code
class Solution3(object):
def reverse_list(self, head):
before = after = None
while head:
after = head.next # 保存當前節點的下一個節點
head.next = before # 將當前節點的下一題個節點替換為當前節點的上一個節點
before = head # 將上一個節點,往前移動,變為當前節點
head = after # 當前節點向前移動
return before # 返回反轉完成后的頭結點
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head and head.next and head == self.reverse_list(head): # 加入反轉后的頭結點與原先的頭結點相同,則說明有環
return True
return False