This commit is contained in:
2026-06-21 13:11:31 +10:00
parent 3bce899398
commit 35c362681e
6 changed files with 533 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
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))
}