LeetCode 142. Linked List Cycle II - 链表(Linked List)系列题17

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值