法一:双指针。 参考讨论区却顾所来径的清晰解释思路。
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。
两个结论: 1、设置快慢指针,假如有环,他们最后一定相遇。 2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
两个结论证明参见讨论区
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
#空链表判断
if pHead==None or pHead.next==None:#注意:'NoneType' object has no attribute 'next'。如果phead为空,不能pHead.next.next
return None
fast=pHead.next.next#如果写fast=pHead,slow=pHead,那么fast直接等于slow,下面不好循环
slow=pHead.next
#第一,先找相遇点
while fast !=slow:
if fast==None:#如果.next遇到空节点,说明走到底了,没有环
return None
fast=fast.next.next
slow=slow.next
#第二,找环入口。两个指针分别从链表头和相遇点继续出发,每次走一步
fast=pHead
while fast !=slow:
fast=fast.next
slow=slow.next
return fast
时间复杂度:O(n),需要将链表的所有结点遍历一遍
空间复杂度:O(1),需要额外两个快慢指针来遍历结点。
法二:哈希表。 最常规易懂的解法,遍历整个链表结点,然后用哈希表(可以用集合)来存储已访问过的结点,最后进行对比。若该结点已存在哈希表中,则代表该结点是我们要找的环形链表的入口结点;否则把结点添加到哈希表中,继续往下遍历。可参考题解区不是江小白的代码。
时间复杂度:O(n) (因为最糟糕的情况就是遍历了整个链表,n 代表链表中的结点数)
空间复杂度:O(n) (最糟糕的情况是把整个链表的结点插入哈希表中)