package top100liked import ( "fmt" "testing" ) // https://leetcode.cn/problems/linked-list-cycle-ii/?envType=study-plan-v2&envId=top-100-liked type ListNode struct { Val int Next *ListNode } // 哈希表 // func detectCycle(head *ListNode) *ListNode { // _map := make(map[*ListNode]bool) // for head != nil { // if _map[head] { // return head // } // _map[head] = true // head = head.Next // } // return nil // } // 快慢指针法 // 核心推导如下: // // 设定变量 // // • a = 头节点到入环点的距离 // • b = 入环点到相遇点的距离 // • c = 相遇点回到入环点的距离(环长 = b + c) // // 推导 // // 相遇时: // // • slow 走了 a + b // • fast 走了 a + b + n(b + c)(多走了 n 整圈) // • 因为 fast 速度是 slow 的 2 倍:2(a + b) = a + b + n(b + c) // // 化简得: // // a + b = n(b + c) // // a = n(b + c) - b // a = (n-1)(b + c) + c // // 等式的含义 // // a = (n-1)(b + c) + c 意味着: // 从头节点走 a 步 = 从相遇点走 (n-1) 整圈 + 再走 c 步 // 而 c 步恰好就是从相遇点回到入环点的距离。 // // 所以 // // • ptr 从头出发,走 a 步到达入环点 // • slow 从相遇点出发,走 (n-1) 圈 + c 步,也到达入环点 // • 两者速度相同、同时出发,必然在入环点相遇 // // 这就是为什么不需要知道 a、b、c 的具体值,只需要让两个指针同速走,就能找到入口。 func detectCycle(head *ListNode) *ListNode { slow, fast := head, head for fast != nil { slow = slow.Next if fast.Next == nil { return nil } fast = fast.Next.Next if fast == slow { p := head for p != slow { p = p.Next slow = slow.Next } return p } } return nil } func Test(t *testing.T) { input := &ListNode{ Val: 3, Next: &ListNode{ Val: 2, Next: &ListNode{ Val: 0, Next: &ListNode{ Val: -4, Next: nil, }, }, }, } input.Next.Next.Next.Next = input.Next fmt.Println(detectCycle(input)) }