Fast & Slow pointers is a technique that uses two pointers moving at different speeds through a linked list. The slow pointer moves one step at a time while the fast pointer moves multiple steps. This difference in speed helps detect cycles and find middle elements in linked lists, similar to how a faster runner would eventually catch up to a slower one on a circular track.
The technique is also called Tortoise and Hare. The slow pointer (tortoise) moves one step at a time, while the fast pointer (hare) moves two. This speed difference lets us find the middle of the list, because the fast pointer reaches the end when the slow pointer is at the middle.
# Definition for singly-linked list node
class ListNode:
def __init__(self, val: int = 0, next: Optional[ListNode] = None):
self.val = val
self.next = next
class FastAndSlowPointers:
def findMiddleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
# Base case: If the head doesn't exist, return None
if not head:
return None
# Initialize fast & slow pointers at the head of the list
slow = head
fast = head
# Move fast pointer two steps and slow pointer one step until fast reaches the end
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Return the slow pointer, which is now at the middle of the list
return slowThe win here is space efficiency. Unlike storing nodes in an array or counting the length first, the tortoise and hare approach finds the middle element in a single pass without using extra space. That makes it the right choice in memory-constrained environments and any problem that needs the middle element of a linked list.
Cycle detection is the original use case for Fast & Slow pointers, used to find loops in a linked list. When a fast pointer moving two steps meets a slow pointer moving one, their intersection confirms the presence of a cycle. This works because in a cycle, the fast pointer eventually catches up to the slow pointer, just like a faster runner laps a slower one on a circular track.
# Definition for singly-linked list node
class ListNode:
def __init__(self, val: int = 0, next: Optional[ListNode] = None):
self.val = val
self.next = next
class FastAndSlowPointers:
def findCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
# Base case: If the head doesn't exist, return None
if not head:
return None
# Initialize fast & slow pointers at the head of the list
slow = head
fast = head
# Move fast pointer two steps and slow pointer one step
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If slow and fast pointers meet, there is a cycle
if slow == fast:
# Find the start of the cycle
cur = head
while cur != slow:
cur = cur.next
slow = slow.next
return cur
# If no cycle exists, return None
return NoneAfter detecting a cycle, we can find its starting point by moving one pointer back to the head of the list and advancing both pointers one step at a time. The point where they meet again marks the beginning of the cycle. The whole thing runs in O(1) extra space, which is what makes it ideal for memory-constrained scenarios.
Fast & Slow pointers work by exploiting speed ratios between two pointers. The slow pointer moves one step while the fast pointer can move any number of steps through a linked list. By adjusting this ratio, we can find different positions. Two steps finds the middle, three steps finds the first third, and so on. This flexibility lets us divide a linked list into any proportion we need, all with the same simple implementation.
The starting position of the pointers affects where slow lands. Starting fast at head.next instead of head causes slow to stop one position earlier: it lands at the left-middle node in even-length lists and one node before the middle in odd-length lists. This variation is especially useful for algorithms that need the left-middle position, like converting a sorted linked list into a balanced binary search tree.
What sets this technique apart is its space complexity. It uses O(1)extra space since we only need two pointers, regardless of the list's size. The time complexity is O(n) because in the worst case, we traverse each node at most twice: once to detect the cycle and once to find its starting point. That's much more efficient than storing visited nodes in a hashmap.
slow and fast pointers at head. Starting fast at head.next instead causes slow to land one position earlier, which is useful for finding the left-middle node in even-length linked lists. Both starting positions work for cycle detection; the position only affects where the pointers meet.slow one step and fast two steps to find the middle. Bumping fast to three steps finds the first third, four steps the first quarter, and so on. Always check that fast and fast.next exist before moving to avoid null pointer exceptions and to detect the end of the list.slow is at the middle when fast reaches null or fast.next is null.slow to headand move both pointers one step at a time until they meet again. That's the cycle start. For middle finding, slow is already at the middle node.Fast & Slow pointers shine on problems that involve linked lists. Reach for this technique whenever you see cycles, middle elements, or problems that require splitting a list. The trick is recognizing when moving pointers at different speeds will reveal something useful about the list's structure.
The two most common variations are same-start problems like cycle detection and offset-start problems like finding the left-middle node. You can also modify the speed ratio: two steps for finding the middle, three steps for finding the first third, and so on. That flexibility is what makes the technique fit so many linked list problems.
Fast & Slow pointers is one of the most useful patterns for linked list problems. Here are a few tips for spotting when to use it and implementing it well:
Fast and slow pointers (Floyds tortoise and hare) detect cycles, find midpoints, and unlock surprising tricks on indices, not just nodes. Work through these to internalize when two pointers moving at different speeds is the right tool:
Completed | Title | Status | Notes | Solution | |
|---|---|---|---|---|---|
Easy | |||||
Easy | |||||
Easy | |||||
Easy | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Hard |