Fast & Slow PointersFloyd's Cycle DetectionLinked ListTwo PointersO(1) Space

Fast & Slow Pointers — The Complete Guide

Master the tortoise-and-hare technique from first principles. Learn to detect cycles, find midpoints, and identify entry points — all in O(1) space — and confidently solve interview questions that rely on pointer-speed asymmetry.

45 min read10 sections
01

Intuition from Scratch

Why does this pattern exist?

Imagine you're walking along a path and you suspect it might loop back on itself. How do you prove it without marking the ground? You can't just keep walking — if there's a loop, you'll walk forever. And you can't mark nodes you've visited without extra memory. The fast-and-slow pointer technique solves this elegantly: send two walkers at different speeds. If there's a cycle, the fast one will eventually lap the slow one — they'll meet. If there's no cycle, the fast one reaches the end.

This is Floyd's Cycle Detection Algorithm, also called the tortoise-and-hare algorithm. The slow pointer moves one step at a time. The fast pointer moves two steps. That speed difference is the entire trick — it guarantees they'll meet inside any cycle, and it lets you find the middle of a list, detect cycles, locate cycle entry points, and more — all in O(1) extra space.

The pattern exists because linked lists don't have random access. You can't jump to the middle or check the length without traversing. Fast-slow pointers give you structural information about the list (middle, cycle, length) using only pointer manipulation — no hash sets, no arrays, no extra memory.

Analogies to Build Intuition

🐢🐇

Tortoise and Hare on a Track

A tortoise and a hare start at the same point on a circular track. The hare runs twice as fast. No matter how big the track is, the hare will eventually lap the tortoise — they'll be at the same spot. If the track is straight (no loop), the hare reaches the end first. That meeting (or not meeting) tells you whether there's a cycle.

⏱️

Clock Hands

The minute hand moves 12× faster than the hour hand. They start together at 12 and meet again roughly every 65 minutes. The speed difference guarantees periodic meetings. Fast-slow pointers work the same way — the speed gap forces a collision inside any cycle.

🏃

Finding the Middle of a Race

Two runners start a race together. One runs at normal speed, the other at double speed. When the fast runner finishes, the slow runner is exactly at the halfway point. That's how fast-slow pointers find the middle of a linked list — no need to know the length.

What kind of problems does it solve?

Fast-slow pointers are your go-to when:

  • You need to detect a cycle in a linked list or sequence
  • You need to find the start of a cycle (the entry node)
  • You need the middle node of a linked list without knowing its length
  • You need to check if a linked list is a palindrome in O(1) space
  • You need to detect cycles in number sequences (Happy Number, finding duplicates)
  • You want O(1) space — no hash sets or visited arrays

The core insight

When two pointers move at different speeds through a structure, the speed difference creates a predictable relationship between their positions. In a cycle, they must meet. In a straight path, the fast pointer reaches the end when the slow pointer is at the middle. This speed asymmetry is the entire foundation of the pattern.

02

Pattern Recognition

Recognizing when fast-slow pointers apply is the most important skill. Here are the signals to look for and the traps to avoid.

Keywords & Signals in the Problem Statement

Use Fast-Slow Pointers when you see...

  • "Detect if a linked list has a cycle"
  • "Find the start/entry of the cycle"
  • "Find the middle node of a linked list"
  • "Check if a linked list is a palindrome" (O(1) space)
  • "Happy number" — detect repeating sequences
  • "Find the duplicate number" in [1, n] with n+1 elements
  • "Reorder list" — needs the middle to split
  • O(1) space constraint on a linked list problem
  • Any problem where a sequence might loop back on itself
  • "Kth node from the end" — variant with fixed gap

Don't use Fast-Slow Pointers when...

  • You need to find pairs that sum to a target — use Two Pointers on sorted data
  • The data is an array with random access — direct indexing is simpler
  • You need to process a contiguous subarray — use Sliding Window
  • You need to detect duplicates in general — use Hashing (O(n) space is fine)
  • The structure has no next-pointer semantics (no linked list, no function sequence)
  • You need to visit all nodes — use BFS/DFS instead

Fast-Slow Pointers vs. Similar Patterns

PatternWhen to UseKey Difference
Fast-Slow PointersCycle detection, finding midpoints, O(1) space linked list opsTwo pointers at different speeds; exploits speed asymmetry
Two Pointers (converging)Sorted arrays, pair sum, container with most waterPointers move toward each other from opposite ends
Two Pointers (same direction)Remove duplicates, partition, sliding windowBoth move forward but at different conditions (not different speeds)
Hashing (for cycle detection)When O(n) space is acceptableStore visited nodes in a set; simpler but uses O(n) space
Linked List reversalPalindrome check, reorder listOften combined with fast-slow: find middle, then reverse second half

The fast-slow question to always ask

When you see a linked list problem with an O(1) space constraint, or any problem where a sequence might cycle, ask: "Can I use two pointers at different speeds?" If the structure has next-pointer semantics (linked list, or a function that maps values to next values), fast-slow pointers almost certainly apply.

03

Brute Force → Optimized Thinking

Let's walk through the thinking evolution using the most classic fast-slow problem: Linked List Cycle Detection — given a linked list, determine if it has a cycle.

1

Brute Force — Hash Set of Visited Nodes

O(n) time · O(n) space

Traverse the list and store every node in a hash set. If you encounter a node that's already in the set, there's a cycle. If you reach null, there's no cycle. Correct, but uses O(n) extra space.

Brute Force — Hash Settypescript
function hasCycleBrute(head: ListNode | null): boolean {
  const visited = new Set<ListNode>();

  let current = head;
  while (current !== null) {
    if (visited.has(current)) return true; // cycle!
    visited.add(current);
    current = current.next;
  }

  return false; // reached null — no cycle
}
// Problem: O(n) space for the hash set.
// Can we detect the cycle without storing every node?
2

Optimal — Floyd's Fast-Slow Pointers

O(n) time · O(1) space

Use two pointers: slow moves 1 step, fast moves 2 steps. If there's a cycle, fast will eventually catch up to slow (they'll meet). If there's no cycle, fast reaches null. Zero extra space.

Floyd's Cycle Detection — Optimaltypescript
function hasCycle(head: ListNode | null): boolean {
  let slow = head;
  let fast = head;

  while (fast !== null && fast.next !== null) {
    slow = slow!.next;          // 1 step
    fast = fast.next.next;      // 2 steps

    if (slow === fast) return true; // they met — cycle!
  }

  return false; // fast reached null — no cycle
}
// O(n) time: fast traverses at most 2n nodes before meeting slow.
// O(1) space: only two pointers, no hash set.
// Why do they meet? In a cycle, fast gains 1 node per step on slow.
// So the gap shrinks by 1 each step → guaranteed collision.

Why Does the Optimization Work?

1

Speed difference guarantees meeting

Once both pointers are inside the cycle, fast gains exactly 1 node on slow per step (fast moves 2, slow moves 1, gap closes by 1). If the cycle has length C, they'll meet within C steps. No matter how big the cycle is.

2

No cycle means fast hits null

If the list is acyclic, fast reaches the end (null) before slow. The while condition (fast !== null && fast.next !== null) catches this cleanly. No infinite loop.

3

The general principle

Whenever you need to detect a cycle in a structure with 'next' semantics, two pointers at different speeds will collide inside the cycle. This replaces O(n) space (hash set) with O(1) space (two pointers). The same principle extends to finding cycle starts, midpoints, and more.

The thinking process matters more than the code

In interviews, walk through this progression: "The brute force uses a hash set to track visited nodes — O(n) space. But if I use two pointers at different speeds, the fast one will lap the slow one inside any cycle. They must meet because the gap closes by 1 each step. O(1) space." This shows the interviewer you understand the math behind the technique.

04

Core Templates

Fast-slow pointer problems follow a few recurring templates. Memorize these — most problems are variations of one of them.

Template 1: Cycle Detection

Detect whether a linked list (or any sequence with next-pointer semantics) contains a cycle. Slow moves 1 step, fast moves 2 steps. If they meet, there's a cycle.

Cycle Detection Templatetypescript
function hasCycle(head: ListNode | null): boolean {
  let slow = head;
  let fast = head;

  while (fast !== null && fast.next !== null) {
    slow = slow!.next;
    fast = fast.next.next;

    if (slow === fast) return true;
  }

  return false;
}
// When to use: "does this linked list have a cycle?"
// Also works for number sequences: next = f(current)
// Key: check fast AND fast.next before advancing

Template 2: Find Cycle Start (Floyd's Phase 2)

After detecting a cycle (pointers meet), find where the cycle begins. Reset one pointer to head, keep the other at the meeting point, then advance both at the same speed. They meet at the cycle start.

Find Cycle Start Templatetypescript
function detectCycleStart(head: ListNode | null): ListNode | null {
  let slow = head;
  let fast = head;

  // Phase 1: Detect cycle (find meeting point)
  while (fast !== null && fast.next !== null) {
    slow = slow!.next;
    fast = fast.next.next;

    if (slow === fast) {
      // Phase 2: Find cycle start
      let entry = head;
      while (entry !== slow) {
        entry = entry!.next;
        slow = slow!.next;
      }
      return entry; // cycle start node
    }
  }

  return null; // no cycle
}
// Why does Phase 2 work?
// Let D = distance from head to cycle start
// Let K = distance from cycle start to meeting point
// At meeting: slow traveled D + K, fast traveled D + K + nC
// Since fast = 2 × slow: D + K + nC = 2(D + K) → D = nC - K
// So moving D steps from head AND from meeting point both reach cycle start.

// When to use: "find where the cycle begins",
// "find the duplicate number" (array as linked list)

Template 3: Find Middle Node

Find the middle of a linked list in one pass. When fast reaches the end, slow is at the middle. For even-length lists, this gives the first of the two middle nodes (or the second, depending on the loop condition).

Find Middle Node Templatetypescript
// Returns the FIRST middle (for even-length lists)
function middleNode(head: ListNode | null): ListNode | null {
  let slow = head;
  let fast = head;

  while (fast?.next !== null && fast?.next?.next !== null) {
    slow = slow!.next;
    fast = fast.next.next;
  }

  return slow;
}

// Returns the SECOND middle (for even-length lists)
function middleNodeSecond(head: ListNode | null): ListNode | null {
  let slow = head;
  let fast = head;

  while (fast !== null && fast.next !== null) {
    slow = slow!.next;
    fast = fast.next.next;
  }

  return slow;
}
// 1 → 2 → 3 → 4 → 5:       both return node 3 (odd length)
// 1 → 2 → 3 → 4 → 5 → 6:   first returns 3, second returns 4
//
// When to use: "find the middle", "split list in half",
// prerequisite for palindrome check and reorder list

Template 4: Linked List Palindrome (Composite Pattern)

Check if a linked list is a palindrome in O(1) space. This combines three techniques: find middle (fast-slow), reverse second half, compare both halves. It's a classic composite pattern.

Palindrome Linked List Templatetypescript
function isPalindrome(head: ListNode | null): boolean {
  if (!head || !head.next) return true;

  // Step 1: Find middle (slow ends at first middle)
  let slow: ListNode | null = head;
  let fast: ListNode | null = head;
  while (fast?.next && fast?.next?.next) {
    slow = slow!.next;
    fast = fast.next.next;
  }

  // Step 2: Reverse second half
  let prev: ListNode | null = null;
  let curr = slow!.next;
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }

  // Step 3: Compare first half and reversed second half
  let left = head;
  let right = prev;
  while (right) {
    if (left!.val !== right.val) return false;
    left = left!.next;
    right = right.next;
  }

  return true;
}
// Time: O(n) — three passes (find middle, reverse, compare)
// Space: O(1) — only pointer manipulation
// When to use: "is this linked list a palindrome?" with O(1) space

Which template to pick?

Ask yourself: (1) Do I need to detect a cycle? → Template 1. (2) Do I need to find where the cycle starts? → Template 2. (3) Do I need the middle of the list? → Template 3. (4) Do I need to check palindrome in O(1) space? → Template 4 (which uses Template 3 as a building block).

05

Variations & Sub-Patterns

Fast-slow pointers aren't limited to basic cycle detection. Here are the most common variations and how the approach adapts for each.

VariationPointer StrategyClassic Example
Cycle DetectionSlow +1, fast +2; meet → cycle existsLinked List Cycle
Cycle StartAfter meeting, reset one to head; advance both +1 until they meetLinked List Cycle II, Find the Duplicate Number
Find MiddleSlow +1, fast +2; when fast ends, slow is at middleMiddle of the Linked List, Sort List
Palindrome CheckFind middle → reverse second half → compare halvesPalindrome Linked List
Cycle in Number SequenceTreat f(x) as 'next pointer'; apply Floyd's on the sequenceHappy Number, Find the Duplicate Number
Reorder ListFind middle → reverse second half → interleaveReorder List
Kth from End (fixed gap)Advance fast K steps first, then move both +1 until fast endsRemove Nth Node From End of List
Cycle LengthAfter meeting, keep one still, advance the other until they meet again; count stepsFind cycle length (sub-step in many problems)

Problems mentioned above

Deep Dive: Happy Number — Cycle in a Number Sequence

A number is "happy" if repeatedly replacing it with the sum of the squares of its digits eventually reaches 1. If it never reaches 1, it enters a cycle. This is Floyd's algorithm applied to a number sequence instead of a linked list — the "next pointer" is the digit-square-sum function.

Happy Number — Floyd's on a Sequencetypescript
function isHappy(n: number): boolean {
  function getNext(num: number): number {
    let sum = 0;
    while (num > 0) {
      const digit = num % 10;
      sum += digit * digit;
      num = Math.floor(num / 10);
    }
    return sum;
  }

  let slow = n;
  let fast = getNext(n);

  while (fast !== 1 && slow !== fast) {
    slow = getNext(slow);           // 1 step
    fast = getNext(getNext(fast));   // 2 steps
  }

  return fast === 1;
}
// If the sequence reaches 1, fast gets there first → return true.
// If the sequence cycles, slow and fast meet → return false.
// Key insight: any bounded sequence that doesn't terminate must cycle.
// Digit square sums are bounded (e.g., 999 → 243), so it must cycle or reach 1.

Deep Dive: Find the Duplicate Number — Array as Linked List

Given an array of n+1 integers where each integer is in [1, n], find the duplicate. The brilliant insight: treat the array as a linked list where nums[i] is the "next pointer" from index i. A duplicate value means two indices point to the same node — that's a cycle. Use Floyd's to find the cycle start.

Find the Duplicate — Array as Linked Listtypescript
function findDuplicate(nums: number[]): number {
  // Treat array as linked list: index → nums[index]
  // A duplicate means two indices map to the same value → cycle

  // Phase 1: Detect cycle (find meeting point)
  let slow = nums[0];
  let fast = nums[0];

  do {
    slow = nums[slow];           // 1 step
    fast = nums[nums[fast]];     // 2 steps
  } while (slow !== fast);

  // Phase 2: Find cycle start (= the duplicate value)
  let entry = nums[0];
  while (entry !== slow) {
    entry = nums[entry];
    slow = nums[slow];
  }

  return entry;
}
// Time: O(n) — Floyd's algorithm
// Space: O(1) — no extra data structures
// Why it works: the duplicate value is the entry point of the cycle.
// Two different indices point to it → it's where paths converge.

Deep Dive: Reorder List — Composite Pattern

Reorder a list from L0 → L1 → ... → Ln to L0 → Ln → L1 → Ln-1 → ... This combines three fast-slow sub-patterns: find middle, reverse second half, then merge/interleave the two halves.

Reorder List — Find Middle + Reverse + Mergetypescript
function reorderList(head: ListNode | null): void {
  if (!head || !head.next) return;

  // Step 1: Find middle
  let slow: ListNode | null = head;
  let fast: ListNode | null = head;
  while (fast?.next && fast?.next?.next) {
    slow = slow!.next;
    fast = fast.next.next;
  }

  // Step 2: Reverse second half
  let prev: ListNode | null = null;
  let curr = slow!.next;
  slow!.next = null; // cut the list
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }

  // Step 3: Interleave (merge alternating)
  let first = head;
  let second = prev;
  while (second) {
    const tmp1 = first!.next;
    const tmp2 = second.next;
    first!.next = second;
    second.next = tmp1;
    first = tmp1;
    second = tmp2;
  }
}
// Time: O(n) — three linear passes
// Space: O(1) — only pointer manipulation
// This is the "composite pattern" — find middle + reverse + merge.
// Many linked list problems decompose into these three building blocks.
06

Visual Walkthrough

Let's trace through the core fast-slow algorithms step by step to see exactly how the pointers move and why they meet.

Cycle Detection — Step by Step

Cycle Detection — Tracetext
List: 123453 (cycle back to node 3)

Positions (node values):
Step 0: slow=1, fast=1                    (start)
Step 1: slow=2, fast=3                    (slow +1, fast +2)
Step 2: slow=3, fast=5                    (slow +1, fast +2)
Step 3: slow=4, fast=4                    (slow +1, fast +234)
        slow === fastCYCLE DETECTED

Why they met at node 4:
- After entering the cycle, fast gains 1 position per step on slow
- Cycle length = 3 (nodes 3, 4, 5)
- Fast was 2 ahead of slow in the cyclemet after 1 more step
- They ALWAYS meet within one full cycle length

Finding Cycle Start — The Math Behind Phase 2

Cycle Start — Trace with Mathtext
List: 123453 (cycle starts at node 3)

D = distance from head to cycle start = 2 (nodes 1, 2)
C = cycle length = 3 (nodes 3, 4, 5)

Phase 1: Find meeting point
  slow traveled: D + K = 2 + 1 = 3 steps (at node 4)
  fast traveled: 2(D + K) = 6 steps (also at node 4)
  K = distance from cycle start to meeting point = 1

Phase 2: Find cycle start
  Reset entry pointer to head (node 1)
  Keep slow at meeting point (node 4)
  
  Step 1: entry=2, slow=5     (both move +1)
  Step 2: entry=3, slow=3     (both move +1)
  entry === slowCYCLE START = node 3

Why Phase 2 works:
  D = nC - K  (from the math: fast = 2 × slow)
  So D steps from head = D steps from meeting point
  Both land on the cycle start.
  
  In our example: D=2, C=3, K=1D = 3-1 = 2

Finding Middle — Step by Step

Find Middle — Tracetext
Odd length: 12345

Step 0: slow=1, fast=1
Step 1: slow=2, fast=3
Step 2: slow=3, fast=5
fast.next is nullSTOP
Middle = slow = node 3 ✓ (exact middle)

Even length: 123456

"Second middle" version (while fast && fast.next):
Step 0: slow=1, fast=1
Step 1: slow=2, fast=3
Step 2: slow=3, fast=5
Step 3: slow=4, fast=null (fast was 5, fast.next=6, fast.next.next=null)
Waitfast=5, fast.next=6, so we continue:
Step 3: slow=4, fast=null (6.next = null, so fast becomes null)
Middle = slow = node 4 ✓ (second of two middles)

"First middle" version (while fast.next && fast.next.next):
Step 0: slow=1, fast=1
Step 1: slow=2, fast=3
Step 2: slow=3, fast=5
fast.next=6, fast.next.next=nullSTOP
Middle = slow = node 3 ✓ (first of two middles)

Key: The loop condition determines which middle you get for even-length lists.
07

Practice Problems

These 7 problems are carefully ordered from easy to hard. Each one teaches a different aspect of the fast-slow pointer technique. Solve them in order.

1

LC 141. Linked List Cycle

Easy

Why this pattern:

Pure cycle detection (Template 1). Slow moves 1 step, fast moves 2 steps. If they meet, there's a cycle. If fast reaches null, there isn't. The most fundamental fast-slow problem — solve this first to build the core intuition.

Key idea: Initialize both pointers at head. Loop while fast and fast.next are not null. Advance slow by 1, fast by 2. If slow === fast, return true. If the loop ends, return false.

2

LC 876. Middle of the Linked List

Easy

Why this pattern:

Find middle (Template 3). When fast reaches the end, slow is at the middle. This problem asks for the second middle node for even-length lists, so use the 'while fast && fast.next' loop condition.

Key idea: Both start at head. Advance slow by 1, fast by 2. When fast is null or fast.next is null, slow is at the middle. For even-length lists, this gives the second middle (node 4 in a 6-node list).

3

LC 202. Happy Number

Easy

Why this pattern:

Cycle detection on a number sequence (Template 1 adapted). The 'next pointer' is the digit-square-sum function. If the sequence reaches 1, it's happy. If it cycles without hitting 1, it's not. This teaches you that fast-slow works on ANY sequence with next-pointer semantics, not just linked lists.

Key idea: Define getNext(n) = sum of squares of digits. Initialize slow = n, fast = getNext(n). Loop: slow = getNext(slow), fast = getNext(getNext(fast)). If fast === 1, return true. If slow === fast, return false (cycle detected).

4

LC 142. Linked List Cycle II

Medium

Why this pattern:

Find cycle start (Template 2 — Floyd's Phase 2). First detect the cycle using fast-slow. Then reset one pointer to head and advance both at speed 1. They meet at the cycle entry. This is the key problem for understanding the math behind Floyd's algorithm.

Key idea: Phase 1: detect cycle (slow and fast meet). Phase 2: reset entry = head. Advance entry and slow both by 1 until they meet. The meeting point is the cycle start. The math: D = nC - K, so D steps from head equals D steps from meeting point.

5

LC 234. Palindrome Linked List

Easy

Why this pattern:

Composite pattern (Template 4). Find middle using fast-slow, reverse the second half, then compare both halves node by node. This combines three techniques: find middle + reverse + compare. It's the gateway to understanding how fast-slow is a building block for larger algorithms.

Key idea: Step 1: find middle (slow at first middle). Step 2: reverse from slow.next onward. Step 3: compare head→...→middle with reversed second half. If all values match, it's a palindrome. O(1) space because reversal is in-place.

6

LC 143. Reorder List

Medium

Why this pattern:

Composite pattern: find middle + reverse second half + interleave. Same building blocks as palindrome check, but the merge step interleaves instead of comparing. This cements the 'find middle → reverse → merge' decomposition.

Key idea: Step 1: find middle, cut the list. Step 2: reverse the second half. Step 3: interleave nodes from first and reversed-second halves alternately. Three linear passes, O(1) space.

7

LC 287. Find the Duplicate Number

Medium

Why this pattern:

Floyd's cycle detection on an array treated as a linked list (Template 2). The array index is the 'node', nums[i] is the 'next pointer'. A duplicate value means two indices point to the same node — creating a cycle. The duplicate is the cycle entry point.

Key idea: Treat the array as a linked list: start at index 0, follow nums[i] as next. Phase 1: slow = nums[slow], fast = nums[nums[fast]] until they meet. Phase 2: reset entry to 0, advance both by 1 until they meet. The meeting point is the duplicate value.

Practice strategy

For each problem: (1) Identify which template applies before coding. (2) Draw the pointer positions for a small example. (3) After solving, write one sentence: "Fast-slow works here because [reason]." This builds the pattern-matching reflex for interviews.

08

Common Mistakes

These are the traps that catch people on fast-slow pointer problems. Learn them here so you don't learn them in an interview.

💥

Null pointer crash on fast.next.next

You advance fast by 2 steps without checking that fast.next exists. If fast is at the last node, fast.next.next crashes with a null reference error.

Always check BOTH conditions: while (fast !== null && fast.next !== null). This ensures fast.next.next is safe. The order matters — check fast first, then fast.next.

🔄

Wrong loop condition for finding middle

You use the wrong while condition and get the wrong middle node for even-length lists. 'while (fast && fast.next)' gives the second middle; 'while (fast.next && fast.next.next)' gives the first middle.

Know which middle you need. For palindrome check and reorder list, you typically want the first middle (to split evenly). For LeetCode 876 (Middle of Linked List), the problem asks for the second middle. Write a comment stating which one you're targeting.

🧮

Forgetting Phase 2 of Floyd's algorithm

You detect the cycle (Phase 1) but then try to return the meeting point as the cycle start. The meeting point is NOT the cycle start — it's just some node inside the cycle.

After Phase 1 (pointers meet), you MUST do Phase 2: reset one pointer to head, keep the other at the meeting point, advance both by 1 until they meet. That meeting point is the cycle start. Don't skip Phase 2.

🔗

Not cutting the list when splitting at the middle

You find the middle node but forget to set middle.next = null to actually split the list into two halves. The second half still points back to the first, causing infinite loops or wrong results.

After finding the middle, save middle.next as the head of the second half, then set middle.next = null. This cleanly separates the two halves. Essential for palindrome check, reorder list, and sort list.

🏁

Starting fast at getNext(head) instead of head

For number sequence problems (Happy Number), you start fast at getNext(n) but for linked list problems you start both at head. Mixing these up causes off-by-one errors or missed cycles.

For linked lists: both start at head, use a while loop. For number sequences: slow = n, fast = getNext(n), use a while loop (or do-while). The difference is because linked list comparison is by reference (same node), while number comparison is by value.

📐

Not understanding the math behind Phase 2

You memorize the algorithm but can't explain WHY resetting to head and advancing both by 1 finds the cycle start. When the interviewer asks, you're stuck.

Understand the proof: slow travels D + K, fast travels 2(D + K) = D + K + nC. So D + K = nC, meaning D = nC - K. Moving D steps from head and D steps from the meeting point (which is K past the cycle start) both land on the cycle start. Practice explaining this in one sentence.

09

Interview Strategy

Knowing the algorithm is half the battle. Here's how to present fast-slow pointer solutions in an interview to maximize your score.

The 5-Step Interview Flow

1

Clarify & Spot the Structure

'This is a linked list — can it have a cycle? Is there a space constraint?' or 'The array has n+1 elements in range [1,n] — that means there must be a duplicate, and I can treat it as a linked list.' — Identify the next-pointer semantics.

2

State the Brute Force

'I could use a hash set to track visited nodes — O(n) space. But since we need O(1) space, I'll use Floyd's cycle detection with two pointers at different speeds.' — Show you know the tradeoff.

3

Explain the Speed Asymmetry

'Slow moves 1 step, fast moves 2 steps. Inside a cycle, fast gains 1 position per step on slow, so they must meet within one cycle length. If there's no cycle, fast reaches null.' — This is the key insight interviewers want to hear.

4

Code It Clean

Write the while condition carefully (check fast AND fast.next). Use clear variable names (slow, fast, entry). If the problem needs Phase 2, add a comment explaining the math: 'D = nC - K, so D steps from head meets D steps from meeting point.'

5

Test & Analyze

Trace through a small example: 'For 1→2→3→4→5→3: slow goes 1,2,3,4 and fast goes 1,3,5,4 — they meet at 4. Phase 2: entry starts at 1, slow at 4. Both advance: (2,5), (3,3) — meet at 3, the cycle start.' State: O(n) time, O(1) space.

Common Follow-Up Questions

Follow-UpHow to Handle
"Why do they meet? Prove it."Inside the cycle, fast gains 1 node per step on slow. If the gap is G, they meet in G steps. Since G < C (cycle length), they always meet within one full traversal of the cycle.
"Why does Phase 2 find the cycle start?"Slow traveled D + K. Fast traveled 2(D + K). The difference is D + K = nC, so D = nC - K. Moving D steps from head and D steps from the meeting point (K past cycle start) both reach the cycle start.
"Can you find the cycle length?"After Phase 1 (pointers meet), keep one pointer still and advance the other one step at a time until they meet again. Count the steps — that's the cycle length.
"What if the fast pointer moves 3 steps instead of 2?"It still works for cycle detection (they'll meet), but the math for Phase 2 changes. Speed 2 is standard because the gap closes by exactly 1 per step, making the proof clean and the meeting guaranteed within C steps.
"Can you restore the list after checking palindrome?"Yes — after comparing, reverse the second half again to restore the original list. This is good practice and shows attention to side effects. Mention it even if the interviewer doesn't ask.
"How does Find the Duplicate work without modifying the array?"Treat index as node, nums[index] as next pointer. Floyd's algorithm only reads the array — never writes. The duplicate is the cycle entry point because two indices map to the same value.

The meta-skill

Interviewers love fast-slow pointer problems because they test mathematical reasoning, not just coding. The ability to explain WHY the pointers meet and WHY Phase 2 finds the cycle start separates strong candidates from those who just memorized the code. Practice the one-sentence explanation: "Fast gains 1 per step, so they meet in at most C steps. Then D = nC - K means D steps from head equals D steps from the meeting point."

10

Summary & Cheat Sheet

Pin this section. Review it before mock interviews and contests.

Quick Revision Cheat Sheet

Core idea: Two pointers at different speeds exploit speed asymmetry to detect cycles, find midpoints, and locate entry points — all in O(1) space

Cycle detection: Slow +1, fast +2. If they meet → cycle. If fast hits null → no cycle. Fast gains 1 per step inside the cycle

Cycle start (Phase 2): After meeting, reset one to head. Both advance +1. They meet at the cycle entry. Math: D = nC - K

Find middle: Slow +1, fast +2. When fast ends, slow is at middle. Loop condition determines first vs second middle

Palindrome check: Find middle → reverse second half → compare halves. Three passes, O(1) space

Number sequences: Any function f(x) creates a sequence. If bounded, it must cycle or terminate. Apply Floyd's with next = f(current)

Array as linked list: Index = node, nums[index] = next pointer. Duplicate value = cycle. Floyd's finds the duplicate as cycle entry

Null safety: Always check: while (fast !== null && fast.next !== null). This prevents null pointer crashes on fast.next.next

Composite pattern: Many problems combine: find middle + reverse + merge/compare. Master each building block separately

Space advantage: Hash set cycle detection: O(n) space. Floyd's: O(1) space. Same O(n) time. The space saving is the whole point

Cycle length: After meeting, keep one still, advance the other until they meet again. Count steps = cycle length

Mental trigger: "Linked list + O(1) space" or "detect cycle" or "find middle" or "sequence that might loop" → Fast-Slow Pointers

Decision Flowchart

When to Use Fast-Slow Pointers — Decision Treetext
Does the problem involve a linked list or a sequence with "next" semantics?
├── NOFast-slow probably doesn't apply.
Consider Two Pointers, Sliding Window, or Hashing.
└── YESDo you need to detect a cycle?
          ├── YESIs O(1) space required?
          │         ├── YESFloyd's Cycle Detection (Template 1)
          │         └── NOHash Set is simpler (but O(n) space)
          └── NODo you need the cycle START?
                    ├── YESFloyd's Phase 1 + Phase 2 (Template 2)
                    └── NODo you need the MIDDLE of the list?
                              ├── YESFast-Slow Find Middle (Template 3)
                              └── NODo you need palindrome check / reorder?
                                        ├── YESComposite: middle + reverse + merge (Template 4)
                                        └── NODo you need Kth from end?
                                                  ├── YESFixed-gap two pointers (advance fast K first)
                                                  └── NOConsider other linked list techniques

Fast-Slow Pointer Complexity Reference

ProblemTimeSpaceTemplate
Cycle DetectionO(n)O(1)Template 1 — basic Floyd's
Cycle StartO(n)O(1)Template 2 — Floyd's Phase 1 + 2
Find MiddleO(n)O(1)Template 3 — fast ends, slow at middle
Palindrome CheckO(n)O(1)Template 4 — middle + reverse + compare
Reorder ListO(n)O(1)Composite — middle + reverse + interleave
Happy NumberO(log n)O(1)Template 1 on number sequence
Find DuplicateO(n)O(1)Template 2 on array-as-linked-list

One last thing

Fast-slow pointers are one of the most elegant patterns in all of DSA. The entire technique rests on one simple idea: when two things move at different speeds through a cycle, they must eventually collide. From that single insight, you get cycle detection, cycle start finding, midpoint location, palindrome checking, and duplicate finding — all in O(1) space. Master the math behind Phase 2 (D = nC - K), and you'll be able to explain and adapt this pattern to any variant an interviewer throws at you.