环形链表Ⅰ只是判断给定的链表中是否存在环形链表,存在则返回True,不存在则返回False
环形链表Ⅱ则是要返回链表开始入环的第一个节点。
一、快慢指针
- 双指针第一次相遇:设两指针fast,slow指向链表头部,fast每轮走2步,slow每轮走1步,如果fast指针走过链表末端,说明链表无环 if not fast or not fast.next :return None
- 当fast==slow时,两个指针在环中第一次相遇,分析fast和slow的步数关系,设链表共有a+b个节点,其中链表头部到链表入口有a个节点(不计链表入口节点)链表环有b个节点,设两指针分别走了f和s步,则有:f=2s(fast走的步数是slow步数的2倍),f=s+nb(fast比slow多走了n个环的长度),得到表达式,f=2nb,s=nb,即fast和slow指针分别走了2n和n个环的周长
- 如果让指针从链表头部一直向前走,并统计步数k,那么所有走到链表入口节点数时的步数是:k=a+nb,而第一次双指针相遇时,slow指针才走了nb步,因此我们需要像一个办法让slow再走a步停下来,就可以到环的入口,这个方法依然使用双指针,slow位置不变,fast重新指向链表头部节点,slow和fast同时每轮向前一步,即此时f=0,s=nb,当fast指针走到f=a时,slow走到s=a+nb,此时两指针重合,并同时指向链表环的入口,返回slow指向的节点
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 快慢指针
fast,slow = head,head
while True:
# 如果fast直接走到链表末端,说明不存在环形链表,则返回None
# if not fast:防止输入为:[] , if not fast.next: 因为fast指针要每次走两步,要确保走一步,因为fast.next.next可以是None
if not fast or not fast.next:
return None
# 在设计第一次相遇的时候,fast走两步,slow走一步
fast,slow = fast.next.next,slow.next
# 如果第一次相遇,则跳出循环,构建第二次相遇,此时f=2nb,s=nb
if fast==slow:
break
# slow还需要走a步,才能到达环形链表头节点
fast = head
while fast!=slow:
fast,slow = fast.next,slow.next
return fast
二、集合做法
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 集合做法
exist = set()
# 如果node不为空
while head:
print(head.val)
# 如果此时的node在集合里
if head in exist:
return head
# 如果node不在集合里
exist.add(head)
head = head.next
return None
三、字典做法
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 字典做法
dic = {}
while head:
# dict.get()
# 返回指定键的值,get(键)
if dic.get(head):
# 如果该点之前被访问过,直接返回该点位置
return head
# 创建键值
dic[head]=1
head = head.next
return None