快慢指针查找入环点

快慢指针查找入环点

成环一定相遇证明

假设一个环上有 N 个节点,有快指针 Fast 与慢指针 Slow 在环上任意两点开始出发,只要 N 是有限的,总有 Fast 超过 Slow 的时候,那么在 Fast 超过 Slow 之前有两种情况:

  1. Fast 在 Slow 之前一个,此时 Fast 在 m+1 的位置,Slow 在 m+2 的位置,下一次运动,Fast 与 Slow 都会运动到 m+3 的位置,Fast 与 Slow 相遇

  2. Fast 在 Slow 之前两个,此时 Fast 在 m 的位置,Slow 在 m+2 的位置,下一次运动,Fast 在 m+2 的位置,Slow 在 m+3 的位置,则会回到情况1,Fast 与 Slow 依然相遇

因此可以得出:Fast 第一次追上 Slow 的时候就会与 Slow 相遇,不会超过 Slow;

可使用该方法证明一个链表上是否有环:

 func hasCycle(head *ListNode) bool {
  if head == nil || head.Next == nil {
  return false
  }
  fastNode, lowNode := head, head
  for fastNode != nil && fastNode.Next != nil {
  lowNode = lowNode.Next
  fastNode = fastNode.Next.Next
  if lowNode == fastNode {
  return true
  }
  }
  return false
 }

入环节点查找

Slow入环时有两种情况:

  1. 假设 Slow 在入环的时候,就与 Fast 相遇了,此时 Fast 已经在环上跑了 n(n>=1) 圈了,此时假设入环之前有 a 个节点,环上有 b 个节点,已知 Fast 运动的长度一定是 Slow 的 2 倍,可知:2×a = a + n×b 则可得出 a = n×b,此时从相遇点出发的指针,与从链表头部出发的指针同时同速运动,则一定会在入环的节点相遇

  2. 假设 Slow 在入环的时候,没有与 Fast 相遇,入环前有 a 个节点,因为此时 Fast 在 Slow 之前,因此 Slow 在入环后没有运行完一圈就会与 Fast 相遇,假设此时 Slow 运行了 b 个节点,距离入环点 c 个节点。此时 Fast 已经在环上跑了 n(n>=1) 圈外加 b 个节点了,此时有 2×(a+b) = a + n×(b+c) +b 转换后有 a = (n-1)×(b+c) + c 。由于 c 为剩下的节点,则刚好 a 为剩下 c 加上整数个环长度,与从链表头部出发的指针同时同速运动,则一定会在入环的节点相遇

以上两种情况下,均会在入环点相遇,因此可证明可使用该方法在查找入环点:

 func detectCycle(head *ListNode) *ListNode {
  if head == nil {
  return nil
  }
  intersect := getIntersect(head)
  if intersect == nil {
  return nil
  }
  ptr1 := head
  ptr2 := intersect
  for ptr1 != ptr2 {
  ptr1 = ptr1.Next
  ptr2 = ptr2.Next
  }
  return ptr1
 }
 
 func getIntersect(head *ListNode) *ListNode {
  if head == nil || head.Next == nil {
  return nil
  }
  fastNode, lowNode := head, head
  for fastNode != nil && fastNode.Next != nil {
  lowNode = lowNode.Next
  fastNode = fastNode.Next.Next
  if lowNode == fastNode {
  return fastNode
  }
  }
  return nil
 }


posted @ 2022-05-02 17:44  小禾先生  阅读(278)  评论(0编辑  收藏  举报