LeetCode Patterns - 05 Fast & Slow Pointer

Post by ailswan June. 02, 2026

中文 ↓

🎯 Fast & Slow Pointer

1️⃣ Core Framework

When discussing Fast & Slow Pointer, I frame it as:

  1. What problem pattern it solves
  2. Why speed difference creates useful information
  3. Linked list cycle detection
  4. Finding the middle of a linked list
  5. Finding cycle entry point
  6. Happy number
  7. Detecting duplicates using cycle logic
  8. Common edge cases
  9. Complexity analysis
  10. Senior-level explanation

2️⃣ What Problem Are We Solving?

Fast and slow pointer is a technique where two pointers move at different speeds.

Usually:

slow pointer moves 1 step
fast pointer moves 2 steps

It is commonly used for:

The core idea is:

If there is a cycle,
a faster pointer will eventually catch a slower pointer.

👉 Interview Answer

Fast and slow pointer is a two-pointer technique where one pointer moves faster than the other.

It is especially useful for linked lists and cycle detection problems.

If a cycle exists, the fast pointer will eventually meet the slow pointer.

If no cycle exists, the fast pointer reaches the end.


3️⃣ Why Fast & Slow Pointer Works


Basic Intuition

Imagine two runners on a circular track.

slow runner: 1 step each time
fast runner: 2 steps each time

If the track is circular:

fast runner eventually catches slow runner

If the path is not circular:

fast runner reaches the end

Linked List View

In a linked list:

Acyclic list:
1 → 2 → 3 → 4 → null

Cyclic list:
1 → 2 → 3 → 4
        ↑     ↓
        6 ← 5

Fast pointer either:

reaches null

or:

meets slow pointer inside the cycle

👉 Interview Answer

The reason fast and slow pointers work is the relative speed difference.

Inside a cycle, fast gains one node on slow at each step.

Since the cycle has finite length, fast must eventually catch slow.

If there is no cycle, fast simply reaches null.


4️⃣ Floyd’s Cycle Detection


Also Called

Tortoise and Hare Algorithm

The slow pointer is the tortoise.

The fast pointer is the hare.


Core Algorithm

slow = head
fast = head

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

    if slow == fast:
        return True

return False

Key Idea

If fast catches slow → cycle exists
If fast reaches null → no cycle

👉 Interview Answer

Floyd’s cycle detection algorithm uses two pointers moving at different speeds.

Slow moves one step at a time.

Fast moves two steps at a time.

If they meet, there is a cycle.

If fast reaches null, there is no cycle.


5️⃣ Linked List Cycle


Problem

Given the head of a linked list, determine whether the linked list has a cycle.


Code

def has_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            return True

    return False

Complexity

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

Why O(n)?

Even though fast moves faster, it does not visit nodes indefinitely before detection.

If there is no cycle:

fast reaches the end

If there is a cycle:

fast meets slow within the cycle

👉 Interview Answer

To detect a linked list cycle, I use fast and slow pointers.

Slow moves one node each time.

Fast moves two nodes each time.

If the list has a cycle, fast eventually meets slow.

If fast reaches null, the list has no cycle.


6️⃣ Finding the Middle of a Linked List


Problem

Given a linked list, return the middle node.

1 → 2 → 3 → 4 → 5
middle = 3

1 → 2 → 3 → 4 → 5 → 6
middle = 4

Code

def middle_node(head):
    slow = head
    fast = head

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

    return slow

Why It Works

When fast moves twice as fast:

fast travels 2x distance
slow travels 1x distance

So when fast reaches the end:

slow is at the middle

👉 Interview Answer

To find the middle of a linked list, I use slow and fast pointers.

Fast moves two steps while slow moves one step.

When fast reaches the end, slow is at the middle node.


7️⃣ First Middle vs Second Middle


Even Length List

For even-length linked lists:

1 → 2 → 3 → 4

There are two middle candidates:

2 and 3

The common template returns the second middle:

3

Return Second Middle

def second_middle(head):
    slow = head
    fast = head

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

    return slow

Return First Middle

def first_middle(head):
    slow = head
    fast = head

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

    return slow

👉 Interview Answer

For even-length linked lists, I clarify whether the problem wants the first middle or the second middle.

The common slow-fast template returns the second middle.

If I need the first middle, I adjust the loop condition so slow stops one node earlier.


8️⃣ Linked List Cycle II


Problem

Given a linked list with a cycle, return the node where the cycle begins.


Algorithm

Step 1: Detect meeting point.

slow and fast meet inside the cycle

Step 2: Move one pointer to head.

ptr1 = head
ptr2 = meeting point

Step 3: Move both one step at a time.

They meet at cycle entry.

Code

def detect_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            break
    else:
        return None

    ptr1 = head
    ptr2 = slow

    while ptr1 != ptr2:
        ptr1 = ptr1.next
        ptr2 = ptr2.next

    return ptr1

👉 Interview Answer

First, I use fast and slow pointers to detect whether a cycle exists.

If they meet, I reset one pointer to the head and keep the other at the meeting point.

Then I move both one step at a time.

The node where they meet again is the cycle entry point.


9️⃣ Why Cycle Entry Logic Works


Define Distances

Let:

a = distance from head to cycle entry
b = distance from cycle entry to meeting point
c = remaining distance from meeting point back to cycle entry

Cycle length:

b + c

When Slow and Fast Meet

Slow travels:

a + b

Fast travels:

2(a + b)

Fast also travels extra full cycles:

a + b + k(b + c)

So:

2(a + b) = a + b + k(b + c)

Therefore:

a + b = k(b + c)

Rearrange:

a = k(b + c) - b
a = (k - 1)(b + c) + c

This means:

distance from head to entry
equals
distance from meeting point to entry
mod cycle length

👉 Interview Answer

The cycle entry proof comes from distance relationships.

When slow and fast meet, fast has traveled twice as far as slow.

The difference between their distances is a multiple of the cycle length.

This implies that the distance from head to cycle entry equals the distance from the meeting point to the cycle entry modulo the cycle length.

So moving one pointer from head and one from the meeting point at the same speed makes them meet at the cycle entry.


🔟 Reorder List


Problem Pattern

Some linked list problems combine fast-slow pointer with reversal and merge.

Example:

1 → 2 → 3 → 4 → 5

Reorder into:

1 → 5 → 2 → 4 → 3

Steps

1. Use fast-slow pointer to find middle.
2. Reverse second half.
3. Merge first half and reversed second half.

Code

def reorder_list(head):
    if not head or not head.next:
        return

    # 1. Find middle
    slow = head
    fast = head

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

    # 2. Reverse second half
    second = slow.next
    slow.next = None

    prev = None
    while second:
        nxt = second.next
        second.next = prev
        prev = second
        second = nxt

    # 3. Merge
    first = head
    second = prev

    while second:
        temp1 = first.next
        temp2 = second.next

        first.next = second
        second.next = temp1

        first = temp1
        second = temp2

👉 Interview Answer

Reorder list is a good example where fast-slow pointer is only the first step.

I use fast-slow pointer to split the list into two halves.

Then I reverse the second half and merge the two halves alternately.

This keeps the solution O(n) time and O(1) extra space.


1️⃣1️⃣ Palindrome Linked List


Problem

Check whether a linked list is a palindrome.

1 → 2 → 2 → 1
true

Steps

1. Find the middle with fast-slow pointer.
2. Reverse the second half.
3. Compare both halves.

Code

def is_palindrome(head):
    if not head or not head.next:
        return True

    # 1. Find middle
    slow = head
    fast = head

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

    # 2. Reverse second half
    prev = None
    current = slow

    while current:
        nxt = current.next
        current.next = prev
        prev = current
        current = nxt

    # 3. Compare
    first = head
    second = prev

    while second:
        if first.val != second.val:
            return False

        first = first.next
        second = second.next

    return True

👉 Interview Answer

For palindrome linked list, I use fast-slow pointer to find the middle.

Then I reverse the second half of the list.

Finally, I compare the first half and reversed second half node by node.

This gives O(n) time and O(1) extra space.


1️⃣2️⃣ Happy Number


Problem

A happy number repeatedly replaces the number by the sum of squares of its digits.

If it eventually reaches 1, it is happy.

If it loops forever, it is not happy.


Example

19
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1

So:

19 is happy

Cycle View

This creates a sequence:

n → next(n) → next(next(n)) → ...

If it does not reach 1, it eventually repeats.

That means:

there is a cycle

Code

def is_happy(n):
    def next_number(x):
        total = 0

        while x > 0:
            digit = x % 10
            total += digit * digit
            x //= 10

        return total

    slow = n
    fast = next_number(n)

    while fast != 1 and slow != fast:
        slow = next_number(slow)
        fast = next_number(next_number(fast))

    return fast == 1

👉 Interview Answer

Happy number can be modeled as cycle detection.

Each number points to the next number generated by summing the squares of its digits.

If the sequence reaches 1, the number is happy.

If it enters a cycle, it is not happy.

Therefore, fast and slow pointers can detect whether the sequence loops.


1️⃣3️⃣ Find Duplicate Number


Problem

Given an array of n + 1 integers where each integer is in the range 1 to n, find the duplicate number.


Key Constraint

nums length = n + 1
values range = 1 to n

This means each value can be treated as a pointer to another index.

index → nums[index]

Because there is a duplicate, there must be a cycle.


Code

def find_duplicate(nums):
    slow = nums[0]
    fast = nums[0]

    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]

        if slow == fast:
            break

    ptr1 = nums[0]
    ptr2 = slow

    while ptr1 != ptr2:
        ptr1 = nums[ptr1]
        ptr2 = nums[ptr2]

    return ptr1

Why It Works

Treat the array as a linked list:

index points to nums[index]

The duplicate value creates two paths into the same node, which forms a cycle.


👉 Interview Answer

Find Duplicate Number can be modeled as cycle detection.

Since values are in the range 1 to n, each value can point to another index.

The duplicate creates a cycle in this implicit linked list.

I use Floyd’s algorithm to find the cycle entry, which corresponds to the duplicate number.


1️⃣4️⃣ Fast & Slow Pointer vs Hash Set


Hash Set Approach

For cycle detection, we can use a set:

visited = set()

while head:
    if head in visited:
        return True

    visited.add(head)
    head = head.next

return False

Trade-Off

Approach Time Space Notes
Hash Set O(n) O(n) Easier to understand
Fast & Slow O(n) O(1) Better space efficiency

👉 Interview Answer

A hash set can also detect cycles by storing visited nodes.

But it requires O(n) extra space.

Fast and slow pointer gives the same O(n) time complexity with O(1) space.

That is why it is preferred when constant space is required.


1️⃣5️⃣ When Fast & Slow Pointer Applies


Good Fit

Use fast and slow pointer when:

There is a linked structure.
There may be a cycle.
The sequence is deterministic.
Each state points to exactly one next state.
We need O(1) extra space.

Examples

Linked list cycle
Cycle entry point
Happy number
Find duplicate number
Middle of linked list
Palindrome linked list
Reorder list

👉 Interview Answer

Fast and slow pointer applies when the problem can be modeled as a sequence of states, where each state deterministically points to the next state.

If the sequence can repeat, then it forms a cycle.

This makes Floyd’s cycle detection a good fit.


1️⃣6️⃣ When Fast & Slow Pointer Does Not Apply


Not Good Fit

Fast and slow pointer is usually not suitable when:

The structure branches.
A state can have multiple next states.
The problem requires shortest path.
The problem requires exploring all possibilities.
The data is not a linked sequence.

Better Alternatives

Problem Type Better Pattern
Graph with multiple neighbors BFS / DFS
Shortest path BFS / Dijkstra
Arbitrary duplicates Hash Set
Sorted array pair search Two Pointers
Contiguous subarray condition Sliding Window

👉 Interview Answer

Fast and slow pointer works best for linear or cyclic sequences.

If each state can branch to multiple next states, then the problem is more like graph traversal.

In that case, BFS or DFS is usually more appropriate.


1️⃣7️⃣ Common Edge Cases


Empty List

head = None

Single Node Without Cycle

1 → null

Single Node With Cycle

1 ↘
↑__|

Two Nodes

1 → 2 → null

or:

1 → 2
↑   ↓
←___

Even vs Odd Length

Important for:

middle node
palindrome linked list
reorder list

👉 Interview Answer

The common edge cases are empty list, single node, single node with a cycle, two-node lists, and even versus odd length.

I pay special attention to loop conditions like fast and fast.next to avoid null pointer errors.


1️⃣8️⃣ Complexity Analysis


Cycle Detection

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

Find Middle

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

Cycle Entry

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

Happy Number

Time: O(log n) per transition, bounded by cycle behavior
Space: O(1)

Find Duplicate Number

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

👉 Interview Answer

Fast and slow pointer solutions are usually O(n) time and O(1) space.

The space benefit is the main advantage compared with hash-set based cycle detection.

Each pointer moves through the sequence a bounded number of times before either reaching the end or detecting a cycle.


1️⃣9️⃣ Common Mistakes


Mistake 1: Null Pointer Error

Wrong:

while fast:
    fast = fast.next.next

Correct:

while fast and fast.next:
    fast = fast.next.next

Mistake 2: Returning Too Early

In cycle detection, compare after both pointers move.

slow = slow.next
fast = fast.next.next

if slow == fast:
    return True

Mistake 3: Confusing Meeting Point With Cycle Entry

The first meeting point is usually not the cycle entry.

You need the second phase:

one pointer from head
one pointer from meeting point
move both one step

Mistake 4: Middle Node Ambiguity

For even-length lists, clarify:

first middle or second middle?

Mistake 5: Applying It to Branching Graphs

Fast-slow pointer assumes a deterministic next state.


👉 Interview Answer

Common mistakes include missing null checks, confusing the meeting point with the cycle entry, using the wrong middle-node template, and applying fast-slow pointer to branching graphs.

I avoid these by defining what fast and slow represent and what condition terminates the loop.


2️⃣0️⃣ Standard Templates


Detect Cycle Template

def has_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            return True

    return False

Find Middle Template

def middle_node(head):
    slow = head
    fast = head

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

    return slow

Find Cycle Entry Template

def detect_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            ptr1 = head
            ptr2 = slow

            while ptr1 != ptr2:
                ptr1 = ptr1.next
                ptr2 = ptr2.next

            return ptr1

    return None

State Cycle Template

def has_state_cycle(start):
    def next_state(x):
        # compute next state
        pass

    slow = start
    fast = next_state(start)

    while slow != fast:
        slow = next_state(slow)
        fast = next_state(next_state(fast))

    return True

🧠 Staff-Level Answer Final


👉 Interview Answer Full Version

Fast and slow pointer is a two-pointer technique where two pointers move through a sequence at different speeds.

Usually, the slow pointer moves one step at a time, and the fast pointer moves two steps at a time.

This pattern is especially useful for linked lists and cycle detection.

The core idea is based on relative speed. If there is a cycle, the fast pointer gains one step on the slow pointer each iteration, so it must eventually meet the slow pointer inside the cycle.

If there is no cycle, the fast pointer reaches null.

The most classic application is Floyd’s cycle detection algorithm, also known as the tortoise and hare algorithm.

It detects whether a linked list has a cycle in O(n) time and O(1) space.

Fast and slow pointer can also find the middle of a linked list. Since fast moves twice as fast as slow, when fast reaches the end, slow is at the middle.

For even-length lists, I clarify whether the problem expects the first middle or the second middle, because the loop condition changes slightly.

For finding the cycle entry point, the first meeting point is not necessarily the entry. After slow and fast meet, I reset one pointer to the head and keep the other at the meeting point. Then I move both one step at a time. Their next meeting point is the cycle entry.

This works because the distance from head to cycle entry equals the distance from the meeting point to the entry modulo the cycle length.

Fast and slow pointer also applies beyond linked lists. Happy Number can be modeled as a sequence of numbers where each number points to the next number. If the sequence repeats, it forms a cycle.

Find Duplicate Number can also be modeled as an implicit linked list, where each index points to nums[index]. The duplicate creates a cycle, and the cycle entry corresponds to the duplicate value.

At a senior level, I would explain that this pattern works when the problem can be modeled as a deterministic sequence of states, where each state points to exactly one next state.

It is not appropriate for branching graphs or problems requiring exploration of multiple paths.

Compared with a hash set approach, fast and slow pointer gives the same O(n) time for cycle detection, but improves space from O(n) to O(1).

The main pitfalls are null pointer errors, confusing the first meeting point with the cycle entry, choosing the wrong middle-node template, and applying the pattern when the state transition is not deterministic.


⭐ Final Insight

Fast & Slow Pointer 的核心不是“一个快一个慢”。

它的核心是利用 relative speed 在 deterministic sequence 中发现结构信息。

Senior-level 的重点是:

sequence 是否 deterministic, 是否可能形成 cycle, fast 为什么一定会追上 slow, meeting point 和 cycle entry 有什么区别, 以及为什么这个方法可以做到 O(1) space。

能讲清楚这些, Fast & Slow Pointer 就不是 linked list 小技巧, 而是一种 cycle detection 的通用思维模型。



中文部分


🎯 Fast & Slow Pointer


1️⃣ 核心框架

讨论 Fast & Slow Pointer 时,我通常从:

  1. 它解决什么问题
  2. 为什么速度差可以产生有用信息
  3. Linked list cycle detection
  4. 找 linked list 中点
  5. 找 cycle entry point
  6. Happy number
  7. 用 cycle logic 找 duplicate
  8. 常见 edge cases
  9. Complexity analysis
  10. 高级工程师解释方式

2️⃣ 要解决什么问题?

Fast and slow pointer 是一种两个 pointer 以不同速度移动的技巧。

通常是:

slow pointer 每次走 1 步
fast pointer 每次走 2 步

它常用于:

核心思想是:

如果存在 cycle,
快指针最终一定会追上慢指针。

👉 面试回答

Fast and slow pointer 是一种 two-pointer 技巧, 其中一个 pointer 比另一个 pointer 移动更快。

它特别适合 linked list 和 cycle detection 问题。

如果存在 cycle,fast pointer 最终会遇到 slow pointer。

如果不存在 cycle,fast pointer 会到达链表结尾。


3️⃣ 为什么 Fast & Slow Pointer 有效?


基本直觉

想象两个跑步者在圆形跑道上:

slow runner: 每次走 1 步
fast runner: 每次走 2 步

如果跑道是圆的:

fast runner 最终会追上 slow runner

如果不是圆的:

fast runner 会到达终点

Linked List 视角

无环 linked list:

1 → 2 → 3 → 4 → null

有环 linked list:

1 → 2 → 3 → 4
        ↑     ↓
        6 ← 5

Fast pointer 要么:

到达 null

要么:

在 cycle 中遇到 slow pointer

👉 面试回答

Fast and slow pointer 有效的原因是 relative speed difference。

在 cycle 内部,fast 每一步都会相对 slow 多走一个节点。

因为 cycle 长度有限, fast 最终一定会追上 slow。

如果没有 cycle,fast 会直接走到 null。


4️⃣ Floyd’s Cycle Detection


又叫:

Tortoise and Hare Algorithm

slow pointer 是 tortoise。

fast pointer 是 hare。


Core Algorithm

slow = head
fast = head

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

    if slow == fast:
        return True

return False

Key Idea

如果 fast 追上 slow → 有 cycle
如果 fast 到达 null → 没有 cycle

👉 面试回答

Floyd’s cycle detection algorithm 使用两个不同速度的 pointer。

slow 每次走一步。

fast 每次走两步。

如果它们相遇,说明存在 cycle。

如果 fast 到达 null,说明不存在 cycle。


5️⃣ Linked List Cycle


Problem

给定 linked list 的 head,判断链表是否有 cycle。


Code

def has_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            return True

    return False

Complexity

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

为什么是 O(n)?

如果没有 cycle:

fast 到达末尾

如果有 cycle:

fast 在 cycle 中遇到 slow

👉 面试回答

判断 linked list 是否有 cycle, 我使用 fast and slow pointers。

slow 每次走一个节点。

fast 每次走两个节点。

如果链表有 cycle,fast 最终会遇到 slow。

如果 fast 到达 null,说明没有 cycle。


6️⃣ 找 Linked List 中点


Problem

给定 linked list,返回 middle node。

1 → 2 → 3 → 4 → 5
middle = 3

1 → 2 → 3 → 4 → 5 → 6
middle = 4

Code

def middle_node(head):
    slow = head
    fast = head

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

    return slow

为什么有效?

fast 的速度是 slow 的两倍:

fast 走 2x 距离
slow 走 1x 距离

所以当 fast 到结尾时:

slow 正好在中点

👉 面试回答

找 linked list 的中点, 我使用 slow 和 fast pointers。

fast 每次走两步, slow 每次走一步。

当 fast 到达链表末尾时, slow 就在 middle node。


7️⃣ First Middle vs Second Middle


偶数长度链表

对于偶数长度:

1 → 2 → 3 → 4

有两个 middle candidates:

2 and 3

常见模板返回 second middle:

3

返回 Second Middle

def second_middle(head):
    slow = head
    fast = head

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

    return slow

返回 First Middle

def first_middle(head):
    slow = head
    fast = head

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

    return slow

👉 面试回答

对偶数长度 linked list, 我会先确认题目需要 first middle 还是 second middle。

常见 slow-fast 模板返回 second middle。

如果需要 first middle, 我会调整 loop condition,让 slow 提前停一个节点。


8️⃣ Linked List Cycle II


Problem

给定一个有 cycle 的 linked list,返回 cycle 开始的节点。


Algorithm

Step 1: 找 meeting point。

slow 和 fast 在 cycle 中相遇

Step 2: 一个 pointer 回到 head。

ptr1 = head
ptr2 = meeting point

Step 3: 两个 pointer 每次都走一步。

它们会在 cycle entry 相遇。

Code

def detect_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            break
    else:
        return None

    ptr1 = head
    ptr2 = slow

    while ptr1 != ptr2:
        ptr1 = ptr1.next
        ptr2 = ptr2.next

    return ptr1

👉 面试回答

首先,我用 fast and slow pointers 判断是否有 cycle。

如果它们相遇, 我把一个 pointer 放回 head, 另一个 pointer 留在 meeting point。

然后两个 pointer 每次都走一步。

它们再次相遇的位置就是 cycle entry。


9️⃣ 为什么 Cycle Entry Logic 正确?


定义距离

设:

a = head 到 cycle entry 的距离
b = cycle entry 到 meeting point 的距离
c = meeting point 回到 cycle entry 的距离

Cycle length:

b + c

Slow 和 Fast 相遇时

Slow 走了:

a + b

Fast 走了:

2(a + b)

Fast 比 slow 多走了若干圈:

a + b + k(b + c)

所以:

2(a + b) = a + b + k(b + c)

因此:

a + b = k(b + c)

变形:

a = k(b + c) - b
a = (k - 1)(b + c) + c

这表示:

head 到 entry 的距离
等于
meeting point 到 entry 的距离,加上若干个完整 cycle

👉 面试回答

Cycle entry 的证明来自距离关系。

当 slow 和 fast 相遇时, fast 走过的距离是 slow 的两倍。

两者距离差一定是 cycle length 的整数倍。

这可以推出: head 到 cycle entry 的距离, 等于 meeting point 到 cycle entry 的距离,mod cycle length。

所以一个 pointer 从 head 出发, 一个 pointer 从 meeting point 出发, 同速移动时会在 cycle entry 相遇。


🔟 Reorder List


Problem Pattern

有些 linked list 问题会把 fast-slow pointer 和 reverse、merge 结合起来。

例如:

1 → 2 → 3 → 4 → 5

重排成:

1 → 5 → 2 → 4 → 3

Steps

1. 用 fast-slow pointer 找中点。
2. Reverse second half。
3. Merge first half and reversed second half。

Code

def reorder_list(head):
    if not head or not head.next:
        return

    # 1. Find middle
    slow = head
    fast = head

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

    # 2. Reverse second half
    second = slow.next
    slow.next = None

    prev = None
    while second:
        nxt = second.next
        second.next = prev
        prev = second
        second = nxt

    # 3. Merge
    first = head
    second = prev

    while second:
        temp1 = first.next
        temp2 = second.next

        first.next = second
        second.next = temp1

        first = temp1
        second = temp2

👉 面试回答

Reorder list 是 fast-slow pointer 和 linked list 操作结合的典型题。

我先用 fast-slow pointer 找到中点,把链表分成两半。

然后 reverse 第二半。

最后把两半交替 merge。

这样可以做到 O(n) time 和 O(1) extra space。


1️⃣1️⃣ Palindrome Linked List


Problem

判断 linked list 是否是 palindrome。

1 → 2 → 2 → 1
true

Steps

1. 用 fast-slow pointer 找中点。
2. Reverse second half。
3. Compare both halves。

Code

def is_palindrome(head):
    if not head or not head.next:
        return True

    # 1. Find middle
    slow = head
    fast = head

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

    # 2. Reverse second half
    prev = None
    current = slow

    while current:
        nxt = current.next
        current.next = prev
        prev = current
        current = nxt

    # 3. Compare
    first = head
    second = prev

    while second:
        if first.val != second.val:
            return False

        first = first.next
        second = second.next

    return True

👉 面试回答

对 palindrome linked list, 我先用 fast-slow pointer 找到中点。

然后 reverse 第二半链表。

最后逐个比较 first half 和 reversed second half。

这样可以做到 O(n) time 和 O(1) extra space。


1️⃣2️⃣ Happy Number


Problem

Happy number 会不断把数字替换成每位数字平方和。

如果最终变成 1,就是 happy number。

如果进入无限循环,就不是。


Example

19
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1

所以:

19 is happy

Cycle View

这会形成一个 sequence:

n → next(n) → next(next(n)) → ...

如果不能到 1,就会重复。

这意味着:

存在 cycle

Code

def is_happy(n):
    def next_number(x):
        total = 0

        while x > 0:
            digit = x % 10
            total += digit * digit
            x //= 10

        return total

    slow = n
    fast = next_number(n)

    while fast != 1 and slow != fast:
        slow = next_number(slow)
        fast = next_number(next_number(fast))

    return fast == 1

👉 面试回答

Happy number 可以建模成 cycle detection。

每个数字通过 digit square sum 指向下一个数字。

如果 sequence 到达 1,说明是 happy。

如果 sequence 进入 cycle,说明不是 happy。

所以可以用 fast and slow pointer 判断是否循环。


1️⃣3️⃣ Find Duplicate Number


Problem

给定一个长度为 n + 1 的数组, 其中每个整数都在 1 到 n 之间, 找出 duplicate number。


Key Constraint

nums length = n + 1
values range = 1 to n

这说明每个 value 都可以被看作指向另一个 index 的 pointer。

index → nums[index]

因为有 duplicate,所以一定会形成 cycle。


Code

def find_duplicate(nums):
    slow = nums[0]
    fast = nums[0]

    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]

        if slow == fast:
            break

    ptr1 = nums[0]
    ptr2 = slow

    while ptr1 != ptr2:
        ptr1 = nums[ptr1]
        ptr2 = nums[ptr2]

    return ptr1

为什么有效?

把 array 看成 linked list:

index points to nums[index]

duplicate value 会让两个路径进入同一个节点, 从而形成 cycle。


👉 面试回答

Find Duplicate Number 可以建模成 cycle detection。

因为 values 都在 1 到 n 之间, 每个 value 都可以指向另一个 index。

Duplicate 会在这个 implicit linked list 中制造一个 cycle。

我使用 Floyd’s algorithm 找 cycle entry, 这个 entry 就是 duplicate number。


1️⃣4️⃣ Fast & Slow Pointer vs Hash Set


Hash Set Approach

对于 cycle detection,可以用 set:

visited = set()

while head:
    if head in visited:
        return True

    visited.add(head)
    head = head.next

return False

Trade-Off

Approach Time Space Notes
Hash Set O(n) O(n) Easier to understand
Fast & Slow O(n) O(1) Better space efficiency

👉 面试回答

Hash set 也可以用来检测 cycle, 方法是记录访问过的 node。

但是它需要 O(n) extra space。

Fast and slow pointer 可以在同样 O(n) time 下, 把 space 降到 O(1)。

所以当题目要求 constant space 时, fast-slow pointer 是更好的选择。


1️⃣5️⃣ 什么时候 Fast & Slow Pointer 适用?


Good Fit

当问题满足:

存在 linked structure
可能存在 cycle
sequence 是 deterministic
每个 state 只有一个 next state
需要 O(1) extra space

Examples

Linked list cycle
Cycle entry point
Happy number
Find duplicate number
Middle of linked list
Palindrome linked list
Reorder list

👉 面试回答

Fast and slow pointer 适用于可以被建模成 state sequence 的问题。

每个 state 都 deterministic 地指向下一个 state。

如果 sequence 可能重复, 那就会形成 cycle。

这时 Floyd’s cycle detection 就很适合。


1️⃣6️⃣ 什么时候 Fast & Slow Pointer 不适用?


Not Good Fit

Fast and slow pointer 通常不适合:

结构会分叉
一个 state 有多个 next states
问题需要 shortest path
问题需要探索所有可能性
数据不是 linked sequence

Better Alternatives

Problem Type Better Pattern
Graph with multiple neighbors BFS / DFS
Shortest path BFS / Dijkstra
Arbitrary duplicates Hash Set
Sorted array pair search Two Pointers
Contiguous subarray condition Sliding Window

👉 面试回答

Fast and slow pointer 最适合 linear 或 cyclic sequence。

如果每个 state 可以走向多个 next states, 那问题更像 graph traversal。

这时候 BFS 或 DFS 通常更合适。


1️⃣7️⃣ 常见 Edge Cases


Empty List

head = None

Single Node Without Cycle

1 → null

Single Node With Cycle

1 ↘
↑__|

Two Nodes

1 → 2 → null

或者:

1 → 2
↑   ↓
←___

Even vs Odd Length

对这些题很重要:

middle node
palindrome linked list
reorder list

👉 面试回答

常见 edge cases 包括 empty list、 single node、 single node with cycle、 two-node list、 以及 even length 和 odd length。

我会特别注意 fast and fast.next 这种 loop condition, 避免 null pointer error。


1️⃣8️⃣ Complexity Analysis


Cycle Detection

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

Find Middle

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

Cycle Entry

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

Happy Number

Time: O(log n) per transition, bounded by cycle behavior
Space: O(1)

Find Duplicate Number

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

👉 面试回答

Fast and slow pointer 通常是 O(n) time 和 O(1) space。

相比 hash set cycle detection, 它最大的优势是节省空间。

每个 pointer 在 detection 完成前, 都只会在 sequence 中移动有限次数。


1️⃣9️⃣ 常见错误


Mistake 1: Null Pointer Error

错误:

while fast:
    fast = fast.next.next

正确:

while fast and fast.next:
    fast = fast.next.next

Mistake 2: 过早比较

Cycle detection 中通常先移动,再比较:

slow = slow.next
fast = fast.next.next

if slow == fast:
    return True

Mistake 3: 把 Meeting Point 当成 Cycle Entry

第一次相遇点通常不是 cycle entry。

还需要第二阶段:

一个 pointer 从 head 出发
一个 pointer 从 meeting point 出发
两个都每次走一步

Mistake 4: Middle Node Ambiguity

偶数长度时要确认:

first middle or second middle?

Mistake 5: 用在 Branching Graph 上

Fast-slow pointer 假设每个 state 只有一个 deterministic next state。


👉 面试回答

常见错误包括 null check 不完整、 把 meeting point 当成 cycle entry、 middle node 模板选错、 以及把 fast-slow pointer 用在 branching graph 上。

我通常会先定义 fast 和 slow 分别表示什么, 以及 loop 的终止条件是什么。


2️⃣0️⃣ 标准模板


Detect Cycle Template

def has_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            return True

    return False

Find Middle Template

def middle_node(head):
    slow = head
    fast = head

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

    return slow

Find Cycle Entry Template

def detect_cycle(head):
    slow = head
    fast = head

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

        if slow == fast:
            ptr1 = head
            ptr2 = slow

            while ptr1 != ptr2:
                ptr1 = ptr1.next
                ptr2 = ptr2.next

            return ptr1

    return None

State Cycle Template

def has_state_cycle(start):
    def next_state(x):
        # compute next state
        pass

    slow = start
    fast = next_state(start)

    while slow != fast:
        slow = next_state(slow)
        fast = next_state(next_state(fast))

    return True

🧠 Staff-Level Answer Final


👉 面试回答完整版本

Fast and slow pointer 是一种 two-pointer technique, 两个 pointer 在 sequence 中以不同速度移动。

通常 slow pointer 每次走一步, fast pointer 每次走两步。

这个 pattern 特别适合 linked list 和 cycle detection。

它的核心来自 relative speed。 如果存在 cycle, fast pointer 每次都会相对 slow pointer 多前进一个节点, 所以最终一定会在 cycle 中追上 slow pointer。

如果不存在 cycle, fast pointer 会到达 null。

最经典的应用是 Floyd’s cycle detection algorithm, 也叫 tortoise and hare algorithm。

它可以在 O(n) time 和 O(1) space 内判断 linked list 是否有 cycle。

Fast and slow pointer 也可以用来找 linked list 的 middle。 因为 fast 的速度是 slow 的两倍, 所以当 fast 到达末尾时, slow 正好在中点。

对偶数长度 linked list, 我会确认题目要 first middle 还是 second middle, 因为 loop condition 会略有不同。

对 cycle entry point, 第一次 meeting point 不一定是 cycle entry。 当 slow 和 fast 相遇后, 我把一个 pointer 放回 head, 另一个 pointer 留在 meeting point。 然后两个 pointer 同速移动。 它们再次相遇的位置就是 cycle entry。

这个结论来自距离关系: head 到 cycle entry 的距离, 等于 meeting point 到 cycle entry 的距离,mod cycle length。

Fast and slow pointer 也可以用于 linked list 之外的问题。 Happy Number 可以看成一个数字 sequence, 每个数字通过 digit square sum 指向下一个数字。 如果 sequence 重复,就形成 cycle。

Find Duplicate Number 也可以建模成 implicit linked list。 每个 index 指向 nums[index]。 Duplicate value 会制造 cycle, cycle entry 就对应 duplicate number。

高级工程师层面, 我会解释这个 pattern 适用于 deterministic sequence: 每个 state 只能指向一个 next state。

如果问题是 branching graph, 或者需要探索多条路径, 那 fast-slow pointer 就不合适, 应该考虑 BFS、DFS 或其他 graph algorithms。

相比 hash set, fast-slow pointer 在 cycle detection 中同样是 O(n) time, 但可以把 space 从 O(n) 降到 O(1)。

主要陷阱是 null pointer error、 混淆 meeting point 和 cycle entry、 middle-node 模板选择错误, 以及在非 deterministic structure 上误用这个 pattern。


⭐ Final Insight

Fast & Slow Pointer 的核心不是“快慢两个指针”。

它的核心是用 relative speed 发现 deterministic sequence 中的 cycle 或结构位置。

Senior-level 的重点是:

sequence 是否 deterministic, 是否可能形成 cycle, fast 为什么一定会追上 slow, meeting point 和 cycle entry 为什么不同, 以及为什么这个方法能做到 O(1) space。

能讲清楚这些, Fast & Slow Pointer 就是一种通用的 cycle detection 思维模型。

Implement