返回链表入环开始的第一个节点。

题目

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

进阶:

你是否可以使用 O(1) 空间解决此题?

示例1

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点

示例2

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例3

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

链表中节点的数目范围是 $[0, 10^4]$

$-10^5 <= Node.val <= 10^5$

pos 为 -1 或者链表中的一个 有效索引 。

题解

方法一:哈希表法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def createLink_rearinsert_cycle(lista,pos):
head = ListNode(-1)
rear = head
for i in range(len(lista)):
new_node=ListNode(lista[i])
if i==pos:
pos_node = new_node
rear.next=new_node
rear=new_node
if pos!=-1:
rear.next=pos_node

return head.next

class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
seen = set()
while head:
if head in seen:
return head
seen.add(head)
head = head.next
return None

if __name__=="__main__":
head = [3,2,0,-4]
pos = 1
new_a=createLink_rearinsert_cycle(head,pos)
a1 = Solution()
p=a1.detectCycle(new_a)
print(p)
<__main__.ListNode object at 0x000001CED72B6A48>

复杂度分析

时间复杂度:O(N),其中 N 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。

空间复杂度:O(N),其中 N 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。

方法二:快慢指针

  • 思路与算法

我们使用两个指针,fastslow。它们起始都位于链表的头部。随后,slow指针每次向后移动一个位置,而 fast指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。

如下图所示,设链表中环外部分的长度为 aslow 指针进入环后,又走了 b的距离与 fast 相遇。此时,fast指针已经走完了环的n圈,因此它走过的总距离为 a+n(b+c)+b=a+(n+1)b+nc

根据题意,任意时刻,fast 指针走过的距离都为 slow指针的 2 倍。因此,我们有

$a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)$

有了 $a=c+(n-1)(b+c)a=c+(n−1)(b+c)$ 的等量关系,我们会发现:从相遇点到入环点的距离加上 n-1 圈的环长,恰好等于从链表头部到入环点的距离。

因此,当发现 slowfast相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def createLink_rearinsert_cycle(lista,pos):
head = ListNode(-1)
rear = head
for i in range(len(lista)):
new_node=ListNode(lista[i])
if i==pos:
pos_node = new_node
rear.next=new_node
rear=new_node
if pos!=-1:
rear.next=pos_node

return head.next
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head or not head.next:
return None

slow = head
fast = head.next

while slow != fast:
if not fast or not fast.next:
return None
slow = slow.next
fast = fast.next.next
ptr=head
while ptr!=slow.next:
ptr=ptr.next
slow=slow.next
return ptr

if __name__=="__main__":
head = [3,2,0,-4]
pos = 1
new_a=createLink_rearinsert_cycle(head,pos)
a1 = Solution()
p=a1.detectCycle(new_a)
print(p)
<__main__.ListNode object at 0x000001CED72B6208>