Lesson 22 — Floyd's Fast and Slow Pointers

~20 minutes · Day 24 · fast/slow pointers · Python

Tonight you solve Linked List Cycle. Interviewers like it because the input looks tiny: just nodes and next pointers. The catch is that a bad loop can run forever. This lesson teaches the interview move that detects a loop without storing every node.

The invariant

Move one pointer twice as fast; inside a cycle the gap shrinks by one each step, so they must eventually meet.

Picture two runners on a circular track. One runner moves one step at a time; the other moves two. Once both are on the track, the faster runner gains exactly one node per loop. If there is a cycle, the distance between them cannot avoid becoming zero forever.

Key insight: fast gains 1 node each loop, so the gap becomes 2 -> 1 -> 0. inside cycle C D E F fast slow gap = 2 one loop later C D E F fast slow gap = 1 next loop C D E F slow + fast meet

Inside the loop, the faster pointer closes the circular gap by one node each iteration.

What Python is checking

A linked list cycle means that following next can bring you back to the same node object again, not merely to another node with the same value (LeetCode statement). In Python, is checks whether two names refer to the same object (Python reference: identity comparisons). That is why the final check is slow is fast, not a value comparison.

Linked List Cycle: the memory way first

The direct approach is to remember every node object you visit in a set. Sets are Python's built-in structure for distinct hashable objects (Python docs: set types).

def hasCycle(head):
    seen = set()                    # nodes visited so far
    cur = head
    while cur:
        if cur in seen:             # same node reached twice
            return True
        seen.add(cur)
        cur = cur.next
    return False

This is O(n) time and O(n) space: fast enough, but the memory can grow with the list. The interview follow-up is the useful part: can you keep the time but remove the set?

Linked List Cycle: the Floyd way

Floyd's cycle-finding algorithm uses two moving references, often called the tortoise and hare (Wikipedia: cycle detection). If there is no cycle, the fast pointer falls off the end. If there is a cycle, fast eventually catches slow.

def hasCycle(head):
    slow = fast = head              # both start at the head
    while fast and fast.next:       # fast can safely move twice
        slow = slow.next            # slow moves one step
        fast = fast.next.next       # fast moves two steps
        if slow is fast:            # same node object
            return True
    return False                    # fast reached the end

The guard while fast and fast.next matters. Without it, fast.next.next can crash on a list with no cycle.

Transfer: same runner, different finish line

Middle of the Linked List: return the middle node of a linked list. Before reading on, predict the move: which pointer should move twice as fast, and where will the slow pointer be when fast reaches the end?

Your prediction first — then read on.
def middleNode(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next            # one step
        fast = fast.next.next       # two steps
    return slow                     # halfway point

Same skeleton, different stopping rule. For cycle detection, meeting means “cycle.” For middle finding, fast reaching the end means slow is halfway through.

Same speed rule: when fast reaches the end, slow is halfway. start 1 2 3 4 5 slow + fast 1 loop 1 2 3 4 5 slow fast 2 loops 1 2 3 4 5 slow = middle fast at end

The middle problem uses the same two speeds, but the signal is the end of the list instead of a meeting point.

What the trade buys you

ApproachTimeSpace
Memory set (remember every node)O(n)O(n)
Floyd fast/slow pointersO(n)O(1)

The time stays linear. The win is that the pointer version uses only two references no matter how long the list is.

Practice — answer before you scroll past

1. What does fast do each loop?

2. Spot the bug in this loop:

while fast:
    slow = slow.next
    fast = fast.next.next

3. Which problem is this pattern?

4. Inside a cycle, what changes each step?

Your move (tonight)

Solve this on LeetCode, logging it in your Notion tracker with the cycle — predict → attempt → compare → encode:

  1. Linked List Cycle — predict the memory-set version first, then try to write Floyd's two-pointer version from memory. For the encode step, write the invariant at the top of this page in your own words.
One primary source

Watch NeetCode — Linked List Cycle after your attempt, as the compare step. Find the exact moment where his pointer reasoning matches or corrects yours.