LeetCode Patterns Pattern 02

Fast & Slow pointers

8 episodes · EP 13–20 · 0 done

02 Explainer

2:14 · 22 beats · narrated

Two pointers at different speeds find cycles and midpoints in O(1) space.

The one-sentence version

Two pointers moving at different speeds through a sequence detect cycles, find midpoints, and locate structure, in O(1) space, without a hash set.

ELI5

Two runners on a track. One jogs, one sprints at double speed.

  • If the track is a loop, the sprinter eventually laps the jogger and they meet. They must, the sprinter gains exactly one step on the jogger every tick, so the gap closes by 1 each time until it hits 0. It can’t be skipped over.
  • If the track is a straight line, the sprinter hits the end and there’s no meeting.

That’s the whole pattern. “Did they meet?” answers is there a cycle? And when the sprinter reaches the end, the jogger is standing exactly halfway: which gives you the midpoint for free, in the same single pass.

How to recognise it

SignalExample
A linked list and you’re asked about a cycleLinked List Cycle I / II
Find the middle of a list in one passMiddle of the Linked List
An implicit sequence: x → f(x) → f(f(x))Happy Number, Find the Duplicate
”Do it in O(1) space” on a list problemthe constraint that rules out a hash set
Reorder / palindrome-check a list in placeReorder List, Palindrome Linked List

The tell: the naive answer is a hash set of visited nodes, O(n) space. Any time the problem says constant space, it’s asking for this.

The hidden version is the one that wins interviews. Happy Number and Find the Duplicate have no linked list anywhere in sight. But n → sum_of_squared_digits(n) and i → nums[i] are functions that map a value to a next value, and iterating a function on a finite set must eventually repeat. That’s a linked list in disguise. Spotting it is the entire trick.

The three shapes

Shape A: Cycle detection

slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
    if slow is fast:
        return True          # they met: there's a cycle
return False                 # fast fell off the end: no cycle

Why while fast and fast.next: fast takes two steps, so you must verify both exist before stepping. Checking only fast crashes on fast.next.next when fast is the last node.

Why they’re guaranteed to meet: once both are inside the loop, fast closes the gap by exactly 1 per iteration. A gap shrinking by 1 can never jump past 0.

Shape B: Finding the cycle’s start (Floyd’s second phase)

After they meet, reset one pointer to the head and move both at the same speed. They meet again at the entrance of the cycle.

slow = head
while slow is not fast:
    slow = slow.next
    fast = fast.next
return slow                  # the node where the cycle begins

Why it works: know this proof, it gets asked:

Let F = distance from head to the cycle entrance, a = distance from the entrance to the meeting point, C = cycle length.

  • slow travelled F + a.
  • fast travelled F + a + nC (it went round the loop n more times).
  • fast moved twice as far: 2(F + a) = F + a + nC → F + a = nC → F = nC − a.

So walking F steps from the head lands you at the same place as walking nC − a steps from the meeting point, which is the entrance. Hence: same speed, they collide there.

Shape C: Midpoint

slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
return slow                  # fast at the end => slow at the middle

The off-by-one to decide consciously: for an even-length list, this returns the second middle ([1,2,3,4] → 3). To get the first middle, start fast = head.next. Both are “correct”; the problem tells you which it wants, and mixing them up is the most common bug in Reorder List and Palindrome Linked List.

Complexity

ShapeTimeSpace
A, cycle detectionO(n)O(1)
B, cycle startO(n)O(1)
C, midpointO(n)O(1)
naive hash-set alternativeO(n)O(n) ← this is what you’re beating

The episodes

EPProblemShapeThe thing it teaches
13LinkedList CycleAThe base case. Why they must meet.
14Start of LinkedList CycleA + BFloyd’s phase 2 and the F = nC − a proof.
15Happy NumberA (implicit)No linked list. A function iterated is a list.
16Find the Duplicate NumberA + B (implicit)i → nums[i] is a linked list. Read-only + O(1).
17Middle of the LinkedListCMidpoint free, and the first/second-middle choice.
18Palindrome LinkedListC + reverseCompose: find middle, reverse half, compare.
19Rearrange a LinkedListC + reverse + mergeThree techniques in one. The composition episode.
20Cycle in a Circular ArrayA (hardest)Direction constraints + per-start-index reset.

What “knowing this in your sleep” means

  1. Why must fast and slow meet inside a cycle? (The gap shrinks by exactly 1 per step, so it must hit 0.)
  2. Why does resetting to head and walking at equal speed find the entrance? (F = nC − a, from 2(F+a) = F+a+nC.)
  3. Why while fast and fast.next and not while fast? (fast.next.next dereferences two links ahead.)
  4. When is a problem with no linked list still this pattern? (Whenever you have next = f(current) over a finite set, iteration must eventually cycle.)
  5. What does this buy over a hash set? (O(1) space instead of O(n). That’s the entire reason the pattern exists.)

Episodes

  1. EP013 LinkedList Cycle Easy · Coming
  2. EP014 Start of LinkedList Cycle Medium · Coming
  3. EP015 Happy Number Medium · Coming
  4. EP016 Find the Duplicate Number Medium · Coming
  5. EP017 Middle of the LinkedList Easy · Coming
  6. EP018 Palindrome LinkedList Medium · Coming
  7. EP019 Rearrange a LinkedList Medium · Coming
  8. EP020 Cycle in a Circular Array Hard · Coming