Given the head
of a linked list, return the node where the cycle begins. If there is no cycle, return null
.
There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next
pointer. Internally, pos
is used to denote the index of the node that tail's next
pointer is connected to (0-indexed). It is -1
if there is no cycle. Note that pos
is not passed as a parameter.
Do not modify the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1 Output: tail connects to node index 1 Explanation: There is a cycle in the linked list, where tail connects to the second node.
Example 2:
Input: head = [1,2], pos = 0 Output: tail connects to node index 0 Explanation: There is a cycle in the linked list, where tail connects to the first node.
Example 3:
Input: head = [1], pos = -1 Output: no cycle Explanation: There is no cycle in the linked list.
Constraints:
- The number of the nodes in the list is in the range
[0, 104]
. -105 <= Node.val <= 105
pos
is-1
or a valid index in the linked-list.
本题还是关于链表环的问题,是LeetCode 141. Linked List Cycle 的拓展,不仅要求检查是否存在环还要求找出环的起始节点。
解法一, 跟LeetCode 141. Linked List Cycle 一样,遍历整个数组,用一个set来记录访问过的节点,要是碰到某一节点已经存在set里,表示环存在并且该节点就是环的起始节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return None
visited = set()
cur = head
while cur:
if cur in visited:
return cur
visited.add(cur)
cur = cur.next
return None
解法二,当然也可以用快慢指针法来解决,但是用快慢指针发现环存在后,该如何确定环的起始节点呢?答案是;让慢指针每次走一步,快指针每次走两步,当两个指针相遇时停下,这时快慢指针所在的位置到环的起始点的距离,跟头节点到环的起始点的距离刚好相等。这时再用一个指针从相遇点,另外一个指针从头节点同时出发每次走一步,它们再次相遇时就是要找的环的起始点。
简单证明一下:假设从头节点到环起始节点(不包括环起点)的节点个数为m, 环内节点个数为n。也就是说从头节点走到环起点需m步,在环里走一圈要n步。
1)当慢指针从头节点出发走了m步到达环起点时,快指针走了2m步,也就说快指针在环外走了m步进入环,然后在环里相对环起点又走了m步。要是环内节点个数为n(为了方便假设n比m大),那此时快指针还需向前走n-m步回到环起始点。
2)此时让快慢指针继续前行,假设慢指针从环起点起又走了k步跟快指针相遇,那么快指针走了2k步。另外在步骤1我们知道快指针再走n-m步会到环起点,而我们又知道从环起点到达相遇点需要k步,也就是说其实快指针走了n-m+k步。
3) 因此可以得出2k=n-m+k,即k=n-m。从环起点走到相遇点要用n-m步,那么从相遇点继续前行需要走n-(n-m) = m步回到环起点。刚好从头节点走到环起点也是m步。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if not fast or not fast.next:
return None
fast, slow = slow, head
while slow != fast:
slow = slow.next
fast = fast.next
return slow