Linked List Cycle II (LeetCode 142)
You're given the head of a linked list that may loop back on itself. Find and return the exact node where that loop begins — or null if the list has no loop at all.
(A test harness may describe the loop internally with a 0-indexed pos marking where the tail's next pointer reconnects, using -1 for no loop, but that index is never handed to your function.)
You must leave the linked list unmodified.
Worked Examples
head = [3,2,0,-4], pos = 1tail connects to node index 1Slow advances 1 step (slow = slow.next) while Fast advances 2 steps (fast = fast.next.next). The distance between them decreases by 1 on every cycle iteration.
Upon collision inside the loop, moving one pointer back to the list head and advancing both at 1 step guarantees they will collide at the exact cycle entrance node.
head = [1,2], pos = 0tail connects to node index 0head = [1], pos = -1no cycle⚖️Formal Constraints & Bounds
The number of the nodes in the list is in the range
[0, 104].-105 <= Node.val <= 105posis-1or a valid index in the linked-list.
Core Insight
The distance from the head to the cycle origin equals the distance from the Phase 1 collision point to the cycle origin (L = k·C - x).
Real-World Scenario & Production Applications
Detecting circular dependencies in build systems, finding recurring loop states in state machines, and reference-counted garbage collection.
Subproblems & Recurrence Decomposition3 Phases
Advance slow at 1x speed and fast at 2x speed until they collide inside the cycle loop, proving a cycle exists. If fast reaches null, terminate early.
slow, fast = head, head
while fast and fast.next:
slow = slow.next; fast = fast.next.next
if slow == fast: break
else: return NoneStep-by-Step Execution Trace Table
| Phase | Step | Ptr 1 Position | Ptr 2 Position | Distance / Action | Status |
|---|---|---|---|---|---|
| Phase 1: Rendezvous | 1-3 | slow at Node(-4) | fast at Node(-4) | Standard Floyd cycle detection | Rendezvous confirmed at Node(-4) |
| Phase 2: Origin Seek | Init | p1 = head (Node 3) | p2 = meeting (Node -4) | Reset one pointer to head; keep other at meeting node | Both step at uniform speed 1 |
| Phase 2: Origin Seek | 1 | p1 = p1.next (Node 2) | p2 = p2.next (Node 2) | Step 1: 3 -> 2; -4 -> 2 | p1 == p2! Cycle entry is Node(2) |
The Math, Step by Step
1. The distances in the list
Count distances in steps, where one step follows one next pointer. Name three numbers:
- is the number of steps from
headto the entrance, the first node of the cycle. This is the node the code must return. - is the cycle length, the number of nodes in the cycle. Walking steps inside the cycle brings you back to the same node.
- is the number of steps from the entrance forward to the node where
slowandfastmeet, with .
The picture shows Example 1. The tail node points back to the node with value 2 at index 1. So the entrance is index 1, , and the cycle holds the three nodes 2, 0 and , so .
2. Where the two pointers are after rounds
Both pointers start at head. In every round of phase 1, slow moves 1 step and fast moves 2 steps. After rounds, slow has moved steps and fast has moved steps.
The number of steps a pointer has moved decides where it stands. A pointer that moved fewer than steps is still before the cycle. A pointer that moved steps is inside the cycle, steps past the entrance. Here is the remainder after dividing by .
3. Why phase 2 stops at the entrance
Suppose the pointers meet after rounds. They stand on the same node inside the cycle. fast has moved steps more than slow. Two positions in the cycle are the same only when the difference in steps is a whole number of laps. So
slow stands steps past the entrance, so and differ by whole laps: for some whole number . Put into this equation:
So is a whole number of laps. This is the key fact: walking steps forward from the meeting point ends at the entrance. The meeting point is steps past the entrance. Walking more steps puts a pointer steps past the entrance, which is a whole number of laps, so it is at the entrance again.
Phase 2 uses this fact. ptr1 starts at head, and ptr2 starts at the meeting point. Both move one step per round. After rounds, ptr1 reaches the entrance, because the entrance is steps from head. ptr2 also reaches the entrance, by the key fact. They cannot meet earlier. Before round , ptr1 is still outside the cycle, and ptr2 is always inside it.
4. Why phase 1 always ends, and the start at head
If the list has no cycle, fast reaches the end first. The loop while fast and fast.next stops, and the code returns None.
If the list has a cycle, slow enters it after rounds. fast is already inside. From then on, fast gains one step on slow in every round. The gap between them inside the cycle shrinks by one step per round, so it reaches within rounds. So phase 1 ends after at most rounds.
Both pointers must start at the same node, head. The proof in part 3 uses "fast has moved exactly twice as many steps as slow". If fast started at head.next, that rule would be off by one step. On Example 1 the two pointers of phase 2 would then never stand on the same node, so the loop while ptr1 != ptr2 would never end.
5. Example 1 by hand
head = [3,2,0,-4], pos = 1, so and . Nodes are named by index.
| Round | slow moved | slow at | fast moved | fast at |
|---|---|---|---|---|
| 1 | 1 | index 1 (value 2) | 2 | index 2 (value 0) |
| 2 | 2 | index 2 (value 0) | 4 | index 1 (value 2) |
| 3 | 3 | index 3 (value ) | 6 | index 3 (value ) |
The pointers meet after rounds, which is . The meeting node, index 3, is steps past the entrance. So , a whole lap. In phase 2, ptr1 moves from index 0 to index 1, and ptr2 moves from index 3 to index 1. They meet after round, at index 1. That is the expected output: the tail connects to node index 1.
6. Example 2 by hand
head = [1,2], pos = 0, so and . After round 1, slow is at index 1 and fast is at index 0. After round 2, both are at index 0, so and . In phase 2, ptr1 starts at head, index 0, and ptr2 starts at the meeting point, also index 0. The loop while ptr1 != ptr2 runs zero rounds, and the code returns index 0, the expected output. This matches .
7. From the proof to the code
| Proof | In detectCycle |
|---|---|
both pointers start at head | slow = fast = head |
| after rounds, 1 step and 2 steps | slow = slow.next and fast = fast.next.next |
| the meeting, after rounds | if slow == fast: |
no cycle: fast reaches the end | while fast and fast.next: then return None |
walk steps from head and from the meeting point | ptr1 = head, ptr2 = slow, then one step each per round |
| both arrive at the entrance together | while ptr1 != ptr2: then return ptr1 |
Phase 1 takes at most rounds and phase 2 takes rounds, so the code takes time for a list of nodes. It keeps four pointers, so it uses extra space and never changes the list.
| 1 | if not head or not head.next: |
| 2 | return None |
| 3 | slow = fast = head |
| 4 | has_cycle = False |
| 5 | while fast and fast.next: |
| 6 | slow = slow.next |
| 7 | fast = fast.next.next |
| 8 | if slow == fast: |
| 9 | has_cycle = True |
| 10 | break |
| 11 | if not has_cycle: |
| 12 | return None |
| 13 | ptr1 = head |
| 14 | ptr2 = slow |
| 15 | while ptr1 != ptr2: |
| 16 | ptr1 = ptr1.next |
| 17 | ptr2 = ptr2.next |
| 18 | return ptr1 |
Conceptual Narrative
Detecting a cycle is the first step; finding its exact entrance needs the second phase of Floyd's algorithm, which rests on the equation .
🧭 Conceptual Foundation & Pattern Intuition
In Linked List Cycle I, we simply answered the binary question: does a cycle exist? We relied on the fact that within a cycle, a 2x runner gains 1 step per tick on a 1x runner, guaranteeing a collision. But knowing that a loop exists doesn't reveal where the straight track ends and the loop begins. Floyd's Phase 2 solves this without any extra memory ( space) by exploiting an exact algebraic equivalence between the straightaway distance and the remaining loop distance.
🏟️ The Analogy: The Track with an Access Road
Imagine a running track shaped like the letter P or a lasso: a straight access road of length leads to a circular track of circumference . You and a friend start at the beginning of the road. Your friend runs twice as fast. When you both enter the circular track, your friend laps you and you collide at some point steps past the entry gate. If someone now places a runner at the very beginning of the access road and tells both runners to jog forward at exactly 1 step per tick, they will arrive at the entry gate at the exact same second! The distance from the start of the road to the gate equals the remaining distance around the loop back to the gate.
🔑 Why It Works
Let:
- = distance from head to cycle entrance
- = circumference (length) of the cycle
- = distance from cycle entrance to the meeting point
When slow and fast collide:
slowtraveled:fasttraveled: (where is the number of full lapsfastran)- Since
fasttravels at double speed: (or ) - Simplifying:
What does mean? It is the exact remaining distance from the meeting point around the cycle back to the entrance gate! Therefore, walking steps from head and walking steps from the collision point (slow) brings both pointers to the cycle entrance simultaneously.
Advancing pointers at different speeds in Phase 2: In Phase 2, both
p1(from head) andp2(from meeting point) must move at exactly 1 step per tick. Moving either at speed 2 skips the rendezvous point.Returning boolean instead of node: LC 142 requires returning the
ListNodeobject where the cycle begins, orNoneif no cycle exists.Failing to return None on acyclic lists: If
fastorfast.nextreachesNone, terminate and returnNoneimmediately before initiating Phase 2.Floyd's Cycle Origin: pointer resets and phase 2 speed. In LC 142 (Linked List Cycle II), after the fast and slow pointers collide in Phase 1, you must reset ONE pointer to
headand leave the other at the collision node, advancing both at 1 step per tick. Resetting both to head re-runs Phase 1 indefinitely; keeping fast at 2x speed causes them to overshoot and cycle past each other.
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
"Return the node where the cycle begins" and a follow-up asking for O(1) memory: a list that may loop, and no room for a visited set. That is Fast & Slow Pointers, plus a second walk to find the entrance.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
fast has always taken twice as many steps as slow, so inside the cycle the gap closes by one per move and they must meet. From there, the cycle's first node is as far from the meeting point as from head, up to whole laps (L = k·C - d, derived in the invariant cards below).
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
while fast and fast.next: testing onlyfastcrashes onfast.next.nextfor every list without a cycle that has an odd number of nodes.if not has_cycle: return Nonebefore phase 2: whenfastruns off the end there is no cycle, and phase 2 would walk intoNone.Phase 2 moves
ptr1andptr2one step each: keepingptr2at two steps is not guaranteed to stop at the entrance.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'd use Fast & Slow Pointers in two phases. First slow moves one step and fast two; inside a cycle fast gains exactly one step per move, so it can't jump over slow and they meet. If fast runs off the end, there's no cycle and I return None. Then I start ptr1 at head and ptr2 at the meeting point and move both one step at a time: the cycle's first node is as far from the meeting point as from head, up to whole laps, so they meet exactly there. The trap is the loop test: while fast and fast.next, or fast.next.next crashes on a list without a cycle. It's
O(N)time andO(1)extra space."
Complexity & Mathematical Proof
O(N)
Look at the execution steps: Phase 1 meets within at most C steps after slow enters loop (at most N steps). Phase 2 takes L steps to reach cycle origin. Total steps <= 2N = O(N).
O(1) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. Only stores two pointer references in O(1) auxiliary space.
Derivation Progression
Fast pointer gains 1 node per step on slow inside the cycle.
Both pointers move at 1 step per iteration across distance L.
O(N) total steps
Strict linear runtime without modifying list nodes.
Variable Definitions
Total number of nodes in the linked list
Distance from head to cycle entrance
Circumference / length of the cycle
Memory Architecture & Bounds
O(1) Iterative
O(1) Pointer references only
O(1) Single node reference
Boundary Best / Worst Cases
only for 0 or 1 node; a list without a cycle still costs (fast walks to the end, about steps).
.
full traversal.
Senior SWE Deconstruction & Hardware Caveats
Triggers: "return the node where the cycle begins", "do not modify the linked list", "O(1) memory". A list that may loop and no room for a visited set: Fast & Slow Pointers, then a second walk from head to find the entrance.
nodes. A visited set also runs in time but stores up to node references; the follow-up asks for memory, which only the two-speed walk gives.
At scale: when each hop is expensive (following a redirect, a symlink or a reference stored on another machine), fast pays two hops per move, so phase 1 costs about three hops per step of slow; that is still far cheaper in memory than remembering every hop, which a long or endless chain can exhaust.
Core Algorithmic State Invariants
- Why the Second Walk Stops at the Entrance
Let L be the distance from head to the cycle's first node, d the distance from there to the meeting point, and C the cycle length. At the meeting, fast has walked twice as far as slow, so 2(L + d) = L + d + k·C for some whole number of laps k, which gives L = k·C - d. From the meeting point, L steps are k·C - d steps: d short of k full laps, which is exactly the cycle's first node. So ptr1 from head and ptr2 from the meeting point, one step each, arrive there together after L steps.
Linked List Cycle II (LeetCode 142)
You're given the head of a linked list that may loop back on itself. Find and return the exact node where that loop begins — or null if the list has no loop at all.
(A test harness may describe the loop internally with a 0-indexed pos marking where the tail's next pointer reconnects, using -1 for no loop, but that index is never handed to your function.)
You must leave the linked list unmodified.
Worked Examples
head = [3,2,0,-4], pos = 1tail connects to node index 1Slow advances 1 step (slow = slow.next) while Fast advances 2 steps (fast = fast.next.next). The distance between them decreases by 1 on every cycle iteration.
Upon collision inside the loop, moving one pointer back to the list head and advancing both at 1 step guarantees they will collide at the exact cycle entrance node.
head = [1,2], pos = 0tail connects to node index 0head = [1], pos = -1no cycle⚖️Formal Constraints & Bounds
The number of the nodes in the list is in the range
[0, 104].-105 <= Node.val <= 105posis-1or a valid index in the linked-list.
Core Insight
The distance from the head to the cycle origin equals the distance from the Phase 1 collision point to the cycle origin (L = k·C - x).
Real-World Scenario & Production Applications
Detecting circular dependencies in build systems, finding recurring loop states in state machines, and reference-counted garbage collection.
Subproblems & Recurrence Decomposition3 Phases
Advance slow at 1x speed and fast at 2x speed until they collide inside the cycle loop, proving a cycle exists. If fast reaches null, terminate early.
slow, fast = head, head
while fast and fast.next:
slow = slow.next; fast = fast.next.next
if slow == fast: break
else: return NoneStep-by-Step Execution Trace Table
| Phase | Step | Ptr 1 Position | Ptr 2 Position | Distance / Action | Status |
|---|---|---|---|---|---|
| Phase 1: Rendezvous | 1-3 | slow at Node(-4) | fast at Node(-4) | Standard Floyd cycle detection | Rendezvous confirmed at Node(-4) |
| Phase 2: Origin Seek | Init | p1 = head (Node 3) | p2 = meeting (Node -4) | Reset one pointer to head; keep other at meeting node | Both step at uniform speed 1 |
| Phase 2: Origin Seek | 1 | p1 = p1.next (Node 2) | p2 = p2.next (Node 2) | Step 1: 3 -> 2; -4 -> 2 | p1 == p2! Cycle entry is Node(2) |
The Math, Step by Step
1. The distances in the list
Count distances in steps, where one step follows one next pointer. Name three numbers:
- is the number of steps from
headto the entrance, the first node of the cycle. This is the node the code must return. - is the cycle length, the number of nodes in the cycle. Walking steps inside the cycle brings you back to the same node.
- is the number of steps from the entrance forward to the node where
slowandfastmeet, with .
The picture shows Example 1. The tail node points back to the node with value 2 at index 1. So the entrance is index 1, , and the cycle holds the three nodes 2, 0 and , so .
2. Where the two pointers are after rounds
Both pointers start at head. In every round of phase 1, slow moves 1 step and fast moves 2 steps. After rounds, slow has moved steps and fast has moved steps.
The number of steps a pointer has moved decides where it stands. A pointer that moved fewer than steps is still before the cycle. A pointer that moved steps is inside the cycle, steps past the entrance. Here is the remainder after dividing by .
3. Why phase 2 stops at the entrance
Suppose the pointers meet after rounds. They stand on the same node inside the cycle. fast has moved steps more than slow. Two positions in the cycle are the same only when the difference in steps is a whole number of laps. So
slow stands steps past the entrance, so and differ by whole laps: for some whole number . Put into this equation:
So is a whole number of laps. This is the key fact: walking steps forward from the meeting point ends at the entrance. The meeting point is steps past the entrance. Walking more steps puts a pointer steps past the entrance, which is a whole number of laps, so it is at the entrance again.
Phase 2 uses this fact. ptr1 starts at head, and ptr2 starts at the meeting point. Both move one step per round. After rounds, ptr1 reaches the entrance, because the entrance is steps from head. ptr2 also reaches the entrance, by the key fact. They cannot meet earlier. Before round , ptr1 is still outside the cycle, and ptr2 is always inside it.
4. Why phase 1 always ends, and the start at head
If the list has no cycle, fast reaches the end first. The loop while fast and fast.next stops, and the code returns None.
If the list has a cycle, slow enters it after rounds. fast is already inside. From then on, fast gains one step on slow in every round. The gap between them inside the cycle shrinks by one step per round, so it reaches within rounds. So phase 1 ends after at most rounds.
Both pointers must start at the same node, head. The proof in part 3 uses "fast has moved exactly twice as many steps as slow". If fast started at head.next, that rule would be off by one step. On Example 1 the two pointers of phase 2 would then never stand on the same node, so the loop while ptr1 != ptr2 would never end.
5. Example 1 by hand
head = [3,2,0,-4], pos = 1, so and . Nodes are named by index.
| Round | slow moved | slow at | fast moved | fast at |
|---|---|---|---|---|
| 1 | 1 | index 1 (value 2) | 2 | index 2 (value 0) |
| 2 | 2 | index 2 (value 0) | 4 | index 1 (value 2) |
| 3 | 3 | index 3 (value ) | 6 | index 3 (value ) |
The pointers meet after rounds, which is . The meeting node, index 3, is steps past the entrance. So , a whole lap. In phase 2, ptr1 moves from index 0 to index 1, and ptr2 moves from index 3 to index 1. They meet after round, at index 1. That is the expected output: the tail connects to node index 1.
6. Example 2 by hand
head = [1,2], pos = 0, so and . After round 1, slow is at index 1 and fast is at index 0. After round 2, both are at index 0, so and . In phase 2, ptr1 starts at head, index 0, and ptr2 starts at the meeting point, also index 0. The loop while ptr1 != ptr2 runs zero rounds, and the code returns index 0, the expected output. This matches .
7. From the proof to the code
| Proof | In detectCycle |
|---|---|
both pointers start at head | slow = fast = head |
| after rounds, 1 step and 2 steps | slow = slow.next and fast = fast.next.next |
| the meeting, after rounds | if slow == fast: |
no cycle: fast reaches the end | while fast and fast.next: then return None |
walk steps from head and from the meeting point | ptr1 = head, ptr2 = slow, then one step each per round |
| both arrive at the entrance together | while ptr1 != ptr2: then return ptr1 |
Phase 1 takes at most rounds and phase 2 takes rounds, so the code takes time for a list of nodes. It keeps four pointers, so it uses extra space and never changes the list.
| 1 | if not head or not head.next: |
| 2 | return None |
| 3 | slow = fast = head |
| 4 | has_cycle = False |
| 5 | while fast and fast.next: |
| 6 | slow = slow.next |
| 7 | fast = fast.next.next |
| 8 | if slow == fast: |
| 9 | has_cycle = True |
| 10 | break |
| 11 | if not has_cycle: |
| 12 | return None |
| 13 | ptr1 = head |
| 14 | ptr2 = slow |
| 15 | while ptr1 != ptr2: |
| 16 | ptr1 = ptr1.next |
| 17 | ptr2 = ptr2.next |
| 18 | return ptr1 |
Conceptual Narrative
Detecting a cycle is the first step; finding its exact entrance needs the second phase of Floyd's algorithm, which rests on the equation .
🧭 Conceptual Foundation & Pattern Intuition
In Linked List Cycle I, we simply answered the binary question: does a cycle exist? We relied on the fact that within a cycle, a 2x runner gains 1 step per tick on a 1x runner, guaranteeing a collision. But knowing that a loop exists doesn't reveal where the straight track ends and the loop begins. Floyd's Phase 2 solves this without any extra memory ( space) by exploiting an exact algebraic equivalence between the straightaway distance and the remaining loop distance.
🏟️ The Analogy: The Track with an Access Road
Imagine a running track shaped like the letter P or a lasso: a straight access road of length leads to a circular track of circumference . You and a friend start at the beginning of the road. Your friend runs twice as fast. When you both enter the circular track, your friend laps you and you collide at some point steps past the entry gate. If someone now places a runner at the very beginning of the access road and tells both runners to jog forward at exactly 1 step per tick, they will arrive at the entry gate at the exact same second! The distance from the start of the road to the gate equals the remaining distance around the loop back to the gate.
🔑 Why It Works
Let:
- = distance from head to cycle entrance
- = circumference (length) of the cycle
- = distance from cycle entrance to the meeting point
When slow and fast collide:
slowtraveled:fasttraveled: (where is the number of full lapsfastran)- Since
fasttravels at double speed: (or ) - Simplifying:
What does mean? It is the exact remaining distance from the meeting point around the cycle back to the entrance gate! Therefore, walking steps from head and walking steps from the collision point (slow) brings both pointers to the cycle entrance simultaneously.
Advancing pointers at different speeds in Phase 2: In Phase 2, both
p1(from head) andp2(from meeting point) must move at exactly 1 step per tick. Moving either at speed 2 skips the rendezvous point.Returning boolean instead of node: LC 142 requires returning the
ListNodeobject where the cycle begins, orNoneif no cycle exists.Failing to return None on acyclic lists: If
fastorfast.nextreachesNone, terminate and returnNoneimmediately before initiating Phase 2.Floyd's Cycle Origin: pointer resets and phase 2 speed. In LC 142 (Linked List Cycle II), after the fast and slow pointers collide in Phase 1, you must reset ONE pointer to
headand leave the other at the collision node, advancing both at 1 step per tick. Resetting both to head re-runs Phase 1 indefinitely; keeping fast at 2x speed causes them to overshoot and cycle past each other.
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
"Return the node where the cycle begins" and a follow-up asking for O(1) memory: a list that may loop, and no room for a visited set. That is Fast & Slow Pointers, plus a second walk to find the entrance.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
fast has always taken twice as many steps as slow, so inside the cycle the gap closes by one per move and they must meet. From there, the cycle's first node is as far from the meeting point as from head, up to whole laps (L = k·C - d, derived in the invariant cards below).
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
while fast and fast.next: testing onlyfastcrashes onfast.next.nextfor every list without a cycle that has an odd number of nodes.if not has_cycle: return Nonebefore phase 2: whenfastruns off the end there is no cycle, and phase 2 would walk intoNone.Phase 2 moves
ptr1andptr2one step each: keepingptr2at two steps is not guaranteed to stop at the entrance.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'd use Fast & Slow Pointers in two phases. First slow moves one step and fast two; inside a cycle fast gains exactly one step per move, so it can't jump over slow and they meet. If fast runs off the end, there's no cycle and I return None. Then I start ptr1 at head and ptr2 at the meeting point and move both one step at a time: the cycle's first node is as far from the meeting point as from head, up to whole laps, so they meet exactly there. The trap is the loop test: while fast and fast.next, or fast.next.next crashes on a list without a cycle. It's
O(N)time andO(1)extra space."
Complexity & Mathematical Proof
O(N)
Look at the execution steps: Phase 1 meets within at most C steps after slow enters loop (at most N steps). Phase 2 takes L steps to reach cycle origin. Total steps <= 2N = O(N).
O(1) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. Only stores two pointer references in O(1) auxiliary space.
Derivation Progression
Fast pointer gains 1 node per step on slow inside the cycle.
Both pointers move at 1 step per iteration across distance L.
O(N) total steps
Strict linear runtime without modifying list nodes.
Variable Definitions
Total number of nodes in the linked list
Distance from head to cycle entrance
Circumference / length of the cycle
Memory Architecture & Bounds
O(1) Iterative
O(1) Pointer references only
O(1) Single node reference
Boundary Best / Worst Cases
only for 0 or 1 node; a list without a cycle still costs (fast walks to the end, about steps).
.
full traversal.
Senior SWE Deconstruction & Hardware Caveats
Triggers: "return the node where the cycle begins", "do not modify the linked list", "O(1) memory". A list that may loop and no room for a visited set: Fast & Slow Pointers, then a second walk from head to find the entrance.
nodes. A visited set also runs in time but stores up to node references; the follow-up asks for memory, which only the two-speed walk gives.
At scale: when each hop is expensive (following a redirect, a symlink or a reference stored on another machine), fast pays two hops per move, so phase 1 costs about three hops per step of slow; that is still far cheaper in memory than remembering every hop, which a long or endless chain can exhaust.
Core Algorithmic State Invariants
- Why the Second Walk Stops at the Entrance
Let L be the distance from head to the cycle's first node, d the distance from there to the meeting point, and C the cycle length. At the meeting, fast has walked twice as far as slow, so 2(L + d) = L + d + k·C for some whole number of laps k, which gives L = k·C - d. From the meeting point, L steps are k·C - d steps: d short of k full laps, which is exactly the cycle's first node. So ptr1 from head and ptr2 from the meeting point, one step each, arrive there together after L steps.