python算法日记(链表系列)_leetcode 142. 环形链表 II

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

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

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii

方法一:利用集合(set)的无序的不重复元素序列的特性。把visited的节点存在set里,延用环形链表 I的做法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

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

方法二:快慢指针。参考LeetCode官方解答
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode/
阶段一:如果是环形链表,慢指针一次走一步,快指针一次走两步,快慢指针总会在环中某一节点相遇。可以想象,在一个环里跑,跑得快的总能追上跑得慢的。因此快慢指针相遇则证明是环形链表。

阶段二:在第一次相遇后,慢指针仍指向相遇位置,快指针指向起点位置。快指针变为只走一步,快慢指针的第二次相遇总是在入口节点处。(这个规律挺难想到的,下面做证明)

image.png

图片来源于 LeetCode官方解答

F为起点到环入口的距离,h为快慢指针相遇的位置。环入口与h把环分为a与b两个部分。

当环很大时,a+b>F的情况:

快慢指针在h点相遇,慢指针走了F+a,快指针比它多走一个环,走了F+a+b+a。因为慢指针走的距离是快指针的一半,2(F+a)=F+2a+b,所以F=b。即多走的环a+b=F+a, b=F 。即快指针在起始位置以步长1走F,慢指针在h位置,以步长1走 b,它们会在入口位置相遇。

当环很小,F距离很长的情况:

环小的话,可能快指针多走n个环。2(F+a)=F+a+n(a+b),F=(n-1)(a+b)+b.

一个走F的距离,一个绕很多圈因为+b还是会走到入口和另一个相遇。

评论里的一个证明:

来源于@振之 https://leetcode-cn.com/u/zhen-zhi/,他分析的是当环很大时,a+b>F的情况。

图一:当慢指针slow走n走到环入口时,快指针fast走2n走到当前位置。

图二:当慢指针走n+b走到当前位置时,当前位置距离环入口n。此时快指针走2(n+b),跟慢指针相遇。让快指针回到起始节点,同走n步就能在环入口相遇。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return None
        slow = fast = head
        while(fast.next.next and slow.next):
            fast = fast.next.next
            slow = slow.next
            if not fast.next or not slow.next:
                return None
            if fast == slow:
                cur = slow
                pre = head
                while(cur!=pre):
                    cur = cur.next
                    pre = pre.next
                return cur
        return None

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值