LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Two Pointers/Fast and Slow Pointers: The Complete Guide for Linked Lists and Arrays

Fast and Slow Pointers: The Complete Guide for Linked Lists and Arrays

LeetCopilot Team
Dec 9, 2025
16 min read
Two PointersFast SlowLinked ListCycle DetectionInterview Prep
Master the tortoise and hare algorithm with comprehensive templates, mathematical proofs, and real-world applications. Learn cycle detection, finding middle elements, and advanced techniques.

The fast and slow pointers pattern—also known as the "tortoise and hare" algorithm—is one of the most elegant solutions in computer science.

It solves problems that seem to require O(n) space with just O(1) space. It detects cycles in linked lists with mathematical certainty. And it finds middle elements without counting.

But here's what makes it powerful: it's not just a trick—it's a fundamental technique with rigorous mathematical backing and dozens of applications.

This comprehensive guide will teach you the complete fast/slow pointers pattern, when to use it, how it works mathematically, and advanced techniques that go beyond basic cycle detection.

TL;DR

Use fast/slow pointers when:

  • Working with linked lists
  • Detecting cycles
  • Finding middle elements
  • Need O(1) space (can't use hash set)
  • Problem involves different speeds of traversal

Template:

python
slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
    if slow == fast:
        return True  # Cycle detected
return False

Time: O(n), Space: O(1)

What Is Fast and Slow Pointers?

The Core Concept

Two pointers traverse a data structure at different speeds:

  • Slow pointer: Moves 1 step at a time
  • Fast pointer: Moves 2 steps at a time

Visual (Linked List):

code
Step 0: S,F -> 1 -> 2 -> 3 -> 4 -> 5 -> None
Step 1: 1 -> S -> 2 -> F -> 3 -> 4 -> 5 -> None
Step 2: 1 -> 2 -> S -> 3 -> 4 -> F -> 5 -> None
Step 3: 1 -> 2 -> 3 -> S -> 4 -> 5 -> F=None

Why It Works

For cycle detection: If there's a cycle, the fast pointer will eventually "lap" the slow pointer, like runners on a circular track.

For finding middle: When fast reaches the end, slow is at the middle.

Mathematical proof: See cycle detection proof.

The Universal Template

Python Template

python
def fast_slow_template(head):
    """
    Template for fast/slow pointers on linked lists.
    
    Args:
        head: Head of linked list
    
    Returns:
        Result based on problem requirements
    """
    # Edge case: empty or single node
    if not head or not head.next:
        return None  # or appropriate default
    
    # Initialize both pointers at head
    slow = head
    fast = head
    
    # Move at different speeds
    # CRITICAL: Check both fast and fast.next
    while fast and fast.next:
        slow = slow.next        # Move 1 step
        fast = fast.next.next   # Move 2 steps
        
        # Check condition (e.g., cycle detection)
        if slow == fast:
            return True  # or appropriate result
    
    # Fast reached end (no cycle)
    return False

JavaScript Template

javascript
function fastSlowTemplate(head) {
    if (!head || !head.next) return null;
    
    let slow = head;
    let fast = head;
    
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
        
        if (slow === fast) {
            return true;
        }
    }
    
    return false;
}

Java Template

java
public boolean fastSlowTemplate(ListNode head) {
    if (head == null || head.next == null) {
        return false;
    }
    
    ListNode slow = head;
    ListNode fast = head;
    
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        
        if (slow == fast) {
            return true;
        }
    }
    
    return false;
}

Pattern 1: Detect Cycle in Linked List

Problem

Determine if a linked list has a cycle.

Solution

python
def hasCycle(head: Optional[ListNode]) -> bool:
    """
    LeetCode #141: Linked List Cycle
    
    Time: O(n)
    Space: O(1)
    """
    slow = fast = head
    
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        
        if slow == fast:
            return True  # Cycle detected
    
    return False  # No cycle

Why It Works

Intuition: If there's a cycle, both pointers will eventually enter it. Once inside, the fast pointer gains 1 position per iteration. Eventually, the gap becomes 0 and they meet.

Mathematical proof:

  • Let cycle length = C
  • When slow enters cycle, fast is somewhere in the cycle
  • Distance between them decreases by 1 each iteration
  • After at most C iterations, distance = 0 (they meet)

See detailed proof: Cycle detection proof

Example Trace

code
List: 3 -> 2 -> 0 -> -4 -> (back to 2)

Step 0: slow=3, fast=3
Step 1: slow=2, fast=0
Step 2: slow=0, fast=2
Step 3: slow=-4, fast=-4  ← MEET! Cycle detected

Pattern 2: Find Cycle Start

Problem

If a cycle exists, find the node where it begins.

Solution

python
def detectCycle(head: Optional[ListNode]) -> Optional[ListNode]:
    """
    LeetCode #142: Linked List Cycle II
    
    Time: O(n)
    Space: O(1)
    """
    # Phase 1: Detect cycle
    slow = fast = head
    
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        
        if slow == fast:
            break  # Cycle detected
    else:
        return None  # No cycle
    
    # Phase 2: Find cycle start
    # Reset slow to head, move both at same speed
    slow = head
    while slow != fast:
        slow = slow.next
        fast = fast.next
    
    return slow  # This is the cycle start

Why It Works

Mathematical insight:

Let:

  • L = distance from head to cycle start
  • C = cycle length
  • k = distance from cycle start to meeting point

When they meet:

  • Slow traveled: L + k
  • Fast traveled: L + k + nC (n complete cycles)

Since fast travels twice as fast:

code
2(L + k) = L + k + nC
L + k = nC
L = nC - k

This means: distance from head to cycle start = distance from meeting point to cycle start (going around the cycle).

So if we reset slow to head and move both at the same speed, they'll meet at the cycle start!

Pattern 3: Find Middle of Linked List

Problem

Find the middle node of a linked list.

Solution

python
def middleNode(head: Optional[ListNode]) -> Optional[ListNode]:
    """
    LeetCode #876: Middle of the Linked List
    
    Time: O(n)
    Space: O(1)
    """
    slow = fast = head
    
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    
    return slow  # Slow is at middle

Why It Works

Intuition: When fast reaches the end (moving 2 steps), slow has moved half as far (1 step), so it's at the middle.

Proof:

  • Fast moves 2x as fast as slow
  • When fast reaches end after n/2 iterations
  • Slow has moved n/2 steps from start
  • Slow is at position n/2 (the middle)

For odd length: Returns the middle node
For even length: Returns the second middle node

Example

code
List: 1 -> 2 -> 3 -> 4 -> 5

Step 0: slow=1, fast=1
Step 1: slow=2, fast=3
Step 2: slow=3, fast=5
Step 3: fast.next=None, stop

Return slow=3 (middle)

Pattern 4: Remove Nth Node From End

Problem

Remove the nth node from the end of a linked list.

Solution

python
def removeNthFromEnd(head: Optional[ListNode], n: int) -> Optional[ListNode]:
    """
    LeetCode #19: Remove Nth Node From End of List
    
    Time: O(n)
    Space: O(1)
    """
    # Use dummy node to handle edge cases
    dummy = ListNode(0)
    dummy.next = head
    
    slow = fast = dummy
    
    # Move fast n+1 steps ahead
    for _ in range(n + 1):
        fast = fast.next
    
    # Move both until fast reaches end
    while fast:
        slow = slow.next
        fast = fast.next
    
    # Remove the node
    slow.next = slow.next.next
    
    return dummy.next

Why It Works

Technique: Maintain a gap of n nodes between fast and slow.

When fast reaches the end, slow is n nodes from the end (the node before the one to remove).

Dummy node trick: Handles the case where we need to remove the head.

Pattern 5: Palindrome Linked List

Problem

Check if a linked list is a palindrome.

Solution

python
def isPalindrome(head: Optional[ListNode]) -> bool:
    """
    LeetCode #234: Palindrome Linked List
    
    Time: O(n)
    Space: O(1)
    """
    if not head or not head.next:
        return True
    
    # Step 1: Find middle using fast/slow
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    
    # Step 2: Reverse second half
    prev = None
    while slow:
        next_node = slow.next
        slow.next = prev
        prev = slow
        slow = next_node
    
    # Step 3: Compare first half and reversed second half
    left, right = head, prev
    while right:  # Only need to check right (shorter half)
        if left.val != right.val:
            return False
        left = left.next
        right = right.next
    
    return True

Why It Works

Three-phase approach:

  1. Find middle with fast/slow
  2. Reverse second half
  3. Compare first half with reversed second half

Space optimization: Instead of copying to an array (O(n) space), we modify the list in-place (O(1) space).

Fast/Slow on Arrays

Pattern: Happy Number

python
def isHappy(n: int) -> bool:
    """
    LeetCode #202: Happy Number
    
    Treat number sequence as a linked list.
    Use fast/slow to detect cycle.
    
    Time: O(log n)
    Space: O(1)
    """
    def get_next(num):
        total = 0
        while num > 0:
            digit = num % 10
            total += digit * digit
            num //= 10
        return total
    
    slow = n
    fast = get_next(n)
    
    while fast != 1 and slow != fast:
        slow = get_next(slow)
        fast = get_next(get_next(fast))
    
    return fast == 1

Why It Works

Key insight: The sequence of numbers forms an implicit linked list. If it doesn't reach 1, it must cycle.

Fast/slow detects the cycle without storing all seen numbers (O(1) space vs O(n) with hash set).

Advanced Techniques

Technique 1: Finding Cycle Length

python
def findCycleLength(head):
    """
    Find the length of the cycle if it exists.
    """
    # Detect cycle
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            break
    else:
        return 0  # No cycle
    
    # Count cycle length
    length = 1
    current = slow.next
    while current != slow:
        length += 1
        current = current.next
    
    return length

Technique 2: Different Speed Ratios

python
def customSpeed(head):
    """
    Fast moves 3 steps, slow moves 1 step.
    
    Note: This can miss cycles in certain cases.
    The 2:1 ratio is optimal.
    """
    slow = fast = head
    
    while fast and fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next.next  # 3 steps
        
        if slow == fast:
            return True
    
    return False

Warning: Speeds other than 2:1 can miss cycles. Stick with the standard 2:1 ratio.

Common Mistakes

Mistake 1: Not Checking fast.next

python
# WRONG
while fast:
    fast = fast.next.next  # Crashes if fast.next is None!

Fix: Always check fast and fast.next

Mistake 2: Wrong Initialization

python
# WRONG: Fast starts ahead
slow = head
fast = head.next  # Crashes if head is None!

Fix: Both start at head, or check if not head or not head.next

Mistake 3: Forgetting Edge Cases

python
# WRONG: Doesn't handle empty list
slow = fast = head
while fast and fast.next:
    # ... (crashes if head is None)

Fix: Check if not head: return None first

See comprehensive guide: Linked list pitfalls

When to Use Fast/Slow

✅ Use When:

  1. Linked list problems
  2. Cycle detection needed
  3. Finding middle without counting
  4. O(1) space required
  5. Implicit linked list (like Happy Number)

❌ Don't Use When:

  1. Arrays where you can use indices
  2. Need to track all elements (use hash set)
  3. Multiple pointers at different positions (use array)
  4. Tree problems (use different traversal)

Practice Problems

Master fast/slow with these problems:

Beginner:

  1. Linked List Cycle (#141) - basic detection
  2. Middle of Linked List (#876) - finding middle
  3. Happy Number (#202) - implicit linked list

Intermediate:
4. Linked List Cycle II (#142) - find cycle start
5. Remove Nth From End (#19) - maintain gap
6. Palindrome Linked List (#234) - multi-step

Advanced:
7. Intersection of Two Linked Lists (#160) - two lists
8. Reorder List (#143) - find middle + reverse
9. Find Duplicate Number (#287) - array as linked list

Complexity Analysis

Time Complexity: O(n)

  • Cycle detection: O(n) - at most 2n steps
  • Find middle: O(n/2) = O(n)
  • Find cycle start: O(n) + O(n) = O(n)

Space Complexity: O(1)

  • Only two pointer variables
  • No additional data structures

Comparison with hash set:

  • Hash set: O(n) time, O(n) space
  • Fast/slow: O(n) time, O(1) space ✓

FAQ

Q: Why does the fast pointer move 2 steps, not 3 or more?

A: Moving 2 steps is optimal. It guarantees detection within C iterations (cycle length). Moving 3+ steps might cause them to miss each other in small cycles.

Q: Can fast and slow start at different positions?

A: Yes, but both starting at head is the standard and simplest approach.

Q: What if the list has only one node?

A: The condition while fast and fast.next handles this—the loop doesn't run.

Q: How do I know if a problem needs fast/slow?

A: Look for keywords: "linked list," "cycle," "middle," or "O(1) space." See pattern recognition.

Conclusion

The fast and slow pointers pattern is a fundamental technique with elegant mathematical properties.

Key principles:

  • Different speeds (2:1 ratio is optimal)
  • Cycle detection with mathematical certainty
  • O(1) space vs hash set's O(n)
  • Multiple applications beyond just cycles

The template:

python
slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
    if slow == fast:
        return True
return False

When to use:

  • Linked lists + cycles
  • Finding middle
  • O(1) space requirement
  • Implicit linked lists

Master this pattern, and you'll solve linked list problems with confidence and elegance. For more patterns, see the complete two pointers guide and opposite direction template.

Next time you see a linked list problem, think: fast and slow pointers.

Want to Practice LeetCode Smarter?

LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.

Also compatible with Edge, Brave, and Opera

Related Tutorials