A singly linked list only lets you walk forward, and you usually don't know its length. Two pointers get around both limits without extra memory. Move them at different speeds (slow 1 step, fast 2 steps) to detect a loop or find the middle. Move them at the same speed with a fixed gap to find the k-th node from the end in one pass.
The idea#
A linked list has no index, so you cannot jump to the middle or count back from the end. Two pointers that move at different speeds, or stay a fixed distance apart, give you that position in one pass.
Tortoise and hare#
- Cycle detection: if the list has a loop, fast eventually laps slow and they land on the same node. If there is no loop, fast reaches
None. - Middle: fast covers twice the distance, so when fast reaches the end, slow is halfway.
Why must they meet? Once both pointers are inside the loop, measure the distance from fast forward to slow. Each step slow moves 1 and fast moves 2, so the gap shrinks by exactly 1. It can't jump over zero, so they meet. The gap starts below the loop length, so this takes at most one lap after slow enters: overall, space.
Finding where the cycle starts (Floyd)#
Knowing there is a loop is often only half the job. The meeting point is not in general where the loop starts. To find the entry, reset one pointer to head and move both one step at a time. Where they meet now is the entry.
Why does this work? Let a be the steps from head to the entry, b the steps from the entry to the meeting point, and c the loop length.
- When they meet, slow has walked
a + band fast has walked2(a + b). - Fast's extra distance,
a + b, was spent going around the loop, so it is a whole number of laps:a + b = k * c. - So
a = k * c - b. Starting at the meeting point (which isbpast the entry), walkingasteps means walkingk * c - b. That lands exactly on the entry. - A pointer starting at
headalso reaches the entry afterasteps. Both arrive together.
Gap pointers#
The second setup keeps both pointers at the same speed and changes only where they start. To find the k-th node from the end, give right a head start of k nodes, then move both together. When right falls off the end, left is k nodes behind it. Start left at a dummy node and it stops just before the target, which is what you need to delete it.
How to spot it#
Each signal maps to one pointer setup.
- "Is there a loop?", "where does the loop start?": fast and slow.
- "Find the middle", "split the list in half": fast and slow.
- "k-th node from the end", "remove the n-th from the end": gap pointers. Move right first, then both.
- "Where do two lists meet?": align the two pointers so they have the same distance left.
- An array whose values are valid indices (
i -> nums[i]) behaves like a linked list, so cycle tricks apply.
Template#
This is Floyd's cycle detection with the entry point. Both pointers must start at head for the a + b argument to hold.
slow = head
fast = head
while fast is not None and fast.next is not None:
slow moves 1 step; fast moves 2 steps
if slow is fast: # phase 1: there is a cycle
slow = head # phase 2: reset one pointer
while slow is not fast:
slow moves 1 step; fast moves 1 step
return slow # they meet at the entry
return None # fast fell off: no cycle
Gap pointers shape#
The gap version from the idea above walks one pointer ahead, then moves both at the same speed.
dummy = new node whose next is head
left = dummy
right = head
repeat k times: right = right.next # right gets a k-node head start
while right is not None:
left = left.next; right = right.next
left now sits just before the k-th node from the end
Which middle?#
Finding the middle uses the same fast and slow loop, but an even-length list has two middles. The start position of fast decides which one you get.
| Start | Loop while | Even length: slow ends on |
|---|---|---|
slow = fast = head |
fast and fast.next |
the second middle |
slow = head, fast = head.next |
fast and fast.next |
the first middle |
Use the first-middle version when you split a list for merge sort: a two-node list then splits 1 + 1 (see Merging & Sorting Lists).
Pitfalls#
These mistakes cause most wrong answers and infinite loops.
- Thinking the meeting point is the loop start. It is only where fast caught slow. The entry needs the reset-and-walk phase.
- Starting fast one step ahead for Floyd.
fast = head.nextis fine for detecting a cycle. It breaks thea + bbookkeeping, though, and phase 2 can loop forever. Start both atheadwhen you need the entry. - Checking
fast.next.nextwithout checkingfast.next. The loop condition must befast and fast.next. - Comparing values instead of nodes. Use
isfor node identity; different nodes can hold equal values. - Gap off by one. Starting
leftatdummy(nothead) leaves it before the target, ready to unlink.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first two.
- 141. Linked List Cycle
- 876. Middle of the Linked List
- 160. Intersection of Two Linked Lists
- 142. Linked List Cycle II
- 19. Remove Nth Node From End of List
- 1721. Swapping Nodes in a Linked List
- 287. Find the Duplicate Number
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Linked List Cycle
You only need phase 1. Return true as soon as slow is fast, and false when fast falls off the end.
Hint: Middle of the Linked List
Check the problem's rule for even lengths. It wants the second middle, so start both pointers at head.
Hint: Intersection of Two Linked Lists
If both lists had the same length, walking in step would meet at the shared node. Either count both lengths and advance the longer list by the difference, or switch heads. With switching, a walks A then B, and b walks B then A. Each walks len(A) + len(B) nodes, so they reach the shared node, or None, at the same moment.
Hint: Linked List Cycle II
Run both phases of the template. Both pointers must start at head, or phase 2 can loop forever.
Hint: Remove Nth Node From End of List
The head might be the node you remove, so start left at a dummy. Give right an n-node head start from head. When right falls off, unlink with left.next = left.next.next and return dummy.next.
Hint: Swapping Nodes in a Linked List
Walk k - 1 steps from the head to get the k-th node from the front. Then use a gap pointer to get the k-th node from the end. Swap their values.
Hint: Find the Duplicate Number
Treat each index i as a node whose next is nums[i]. The duplicate value is the one two indices point to, so it is the entry of a cycle. Run Floyd from index 0.
Key takeaways#
- Different speeds: loops and middles. Same speed with a gap: k-th from the end.
- Inside a loop the gap shrinks by one per step, so fast always catches slow in .
- The meeting point is not the entry; reset one pointer to
headand walk both one step at a time. - Start gap pointers from a dummy so the left one stops before the node to delete.
- Compare nodes with
is, not by value.