链表 7. 环形链表II

链表 7. 环形链表II

142. 环形链表 II - 力扣(LeetCode)

代码随想录

难度 2 - 中等

  • 放弃分析。直接看代码随想录的解析,比较详细且透彻。

  • 要点理解:

    整体分为两部分。

    1. 快慢指针追赶,以判定是否有环。

      • 快指针和慢指针同时从head出发,快指针一次走两步,慢指针一次走一步。
      • 如果有环存在,则两者必然入环;
      • 那么入环之后,就必然发生快指针对慢指针的追及;
      • 因此两者必然相遇于环内某一结点M。
    2. 双指针分别从head和相遇点出发,同速一次一步前进,两者的首次相遇必在入环点。

      具体相关证明见代码随想录。

      个人的一些理解:

      设head到入环点前步数m,入环点到点M步数x,点M再到入环点步数y。

      第一部分中:

      快指针走了:2t = m + p(x+y) + x;

      慢指针走了:t = m + q(x+y) + x;

      因此消掉t得到:m + x = (p-2q)(x+y);

      也就是说:m = (p-2q)(x+y) - x = (p-2q-1)(x+y) + y;

      易知快慢指针相遇时,慢指针最多只能刚好走完一圈环,不可能超出,也就是说q = 0(具体原因也可以详见代码随想录);

      而快指针为了追上慢指针,必有p >= 1;

      因此可有:m = (p-1)(x+y) + y。

      也就是说,第二部分中:

      指针一从head出发走m步,

      等价于指针二从M点出发走y步+(p-1)圈环,

      两同速指针将同时落在入环点。

  • 代码

    # 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]:
            # dummy_head = ListNode(next=head)
            slow = head
            fast = head
    
            flag = 0 # 为0时无环,为1时有环
            # 如果非环形那么fast肯定先到达none,可以直接结束循环
            while fast is not None and fast.next is not None: 
                slow = slow.next
                fast = fast.next.next
                if slow == fast:
                    flag = 1
                    break
    
    		# 无环情况
            if flag == 0:
                return None
    
    		# 有环情况
            node_1 = head
            node_2 = slow
            while node_1 != node_2:
                node_1 = node_1.next
                node_2 = node_2.next
            return node_1
    
  • 时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n

  • 空间复杂度: O(1)

  • 同方法代码方面优化版本:

    class Solution:
        def detectCycle(self, head: ListNode) -> ListNode:
            slow = head
            fast = head
    
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
    
                # If there is a cycle, the slow and fast pointers will eventually meet
                if slow == fast:
                    # Move one of the pointers back to the start of the list
                    slow = head
                    while slow != fast:
                        slow = slow.next
                        fast = fast.next
                    return slow
            # If there is no cycle, return None
            return None
    

你可能感兴趣的:(小白的代码随想录刷题笔记,Mophead的小白刷题笔记,leetcode,代码随想录,链表)