A linked list with a cycle has no end. Walk it and you walk forever, passing nodes that all look brand new. The obvious fix is to record every visited node in a hashmap and watch for a repeat, paying O(n) extra memory for the notebook. Fast & Slow pointers prove the same cycle exists while remembering exactly two addresses. How do you confirm you're going in circles without keeping any notes?
The answer is speed. The technique uses two pointers moving at different speeds through the 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 on a circular track eventually laps a slower one. The runner picture has one misleading spot: runners glide past each other mid-stride, while pointers hop from node to node, and whether a hop can skip over the other pointer is a real question this page settles below.
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. Before reading past the code below, commit to a prediction: the six-node list [1, 2, 3, 4, 5, 6] has two middle candidates, the 3 and the 4. Which one does findMiddleNode return?
# 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 slowIt returns the 4. Trace it: slow walks 1 → 2 → 3 → 4 while fast jumps 1 → 3 → 5 and then off the end of the list. After three rounds the loop condition fails and slow is sitting on the right-middle node. If you want the left-middle 3 instead, start fast at head.next. The 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. You might think the hare could leap clean over the tortoise, the two orbiting forever without ever landing on the same node. The arithmetic rules it out: once both pointers are inside the cycle, every tick moves fast two nodes and slow one, so the gap between them shrinks by exactly one node per step. A gap that counts down 3, 2, 1, 0 cannot skip the 0, which means the meeting is forced.
# 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, two pointers and no notebook in sight.
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 head and 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 to carve out whatever fraction of the list the problem wants. 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:
Before you start, quiz yourself on two things from this page: once both pointers are inside a cycle, why can't the hare jump over the tortoise without meeting it, and which middle node does slow land on in an even-length list when both pointers start at head? If both answers come back quickly, you're ready. Fast and slow pointers (Floyd's tortoise and hare) detect cycles, find midpoints, and pull off 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 |