Linked Lists · Linked List Fundamentals

Two-Pointer List Traversal

Walking a linked list with two references moving at different paces or offsets - fast/slow to find the middle, or a fixed gap to find a position counted from the end - all without knowing the list's length up front.

  • 5 min read
  • 9 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The approach: fast/slow to find the middle
  3. A second flavor: fixed gap to find a position from the end
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Two pointers on a linked list find positions you'd otherwise need the length for. A slow pointer moving one step and a fast pointer moving two find the middle in one loop. Two pointers kept a fixed n nodes apart find the n-th node from the end. They're the building blocks of Middle of the Linked List, Remove Nth Node From End, palindrome checks, and splitting a list for merge sort.

We'll go from simple to more technical:

  1. The pattern: fast and slow pointers find the middle.
  2. A second flavor: a fixed gap finds a position from the end.
  3. Where two-speed and fixed-gap walks show up elsewhere.
  4. The even-length and remove-the-head edge cases.

The approach: fast/slow to find the middle

The obvious way to find the middle is to count the nodes, then walk half that far. Two pointers do it in one loop with no counter. fast moves two steps for each step of slow. So when fast reaches the end, slow has gone exactly half as far: it's at the middle.

Finding the middle of 3 -> 8 -> 1 -> 6 -> 2:

Stepslowfast
Start33
181
212: fast.next is null, so stop

The middle is 1. To be precise about the gain: the two pointers don't do less walking than counting first. What they remove is the separate counting pass and the length variable.

Java

ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
    slow = slow.next;
    fast = fast.next.next;
}
// slow is now at the middle - fast has traveled twice as far,
// so when fast reaches the end, slow is exactly halfway.

Python

slow = fast = head
while fast is not None and fast.next is not None:
    slow = slow.next
    fast = fast.next.next
# slow is now at the middle - fast has traveled twice as far,
# so when fast reaches the end, slow is exactly halfway.

On an even-length list there are two middles. For 3 -> 8 -> 1 -> 6, this loop stops with slow on 1, the second one.

A second flavor: fixed gap to find a position from the end

To remove the n-th node from the end, move fast n steps ahead first. Then move both pointers together until fast reaches the last node. slow is then n nodes behind it: just before the node to remove. Both start at a dummy node placed before the head, so removing the head itself needs no special case.

Removing the 2nd node from the end of 3 -> 8 -> 1 -> 6 -> 2: fast first moves 2 steps, to 8. Then both move until fast is on 2, the last node. slow ends on 1, so slow.next = slow.next.next unlinks 6, leaving 3 -> 8 -> 1 -> 2.

Java

ListNode fast = dummy, slow = dummy;   // dummy.next = head, handles removing the head cleanly
for (int i = 0; i < n; i++) fast = fast.next;   // open up a gap of n nodes
while (fast.next != null) {
    fast = fast.next;
    slow = slow.next;
}
// slow is now just BEFORE the nth node from the end - its predecessor,
// so slow.next = slow.next.next removes it

Python

fast = slow = dummy               # dummy.next = head, handles removing the head cleanly
for _ in range(n):
    fast = fast.next              # open up a gap of n nodes
while fast.next is not None:
    fast = fast.next
    slow = slow.next
# slow is now just BEFORE the nth node from the end - its predecessor,
# so slow.next = slow.next.next removes it

Both patterns answer a question about position relative to the length, in one loop, without ever knowing the length.

Where you'll see it

The two-speed idea is also how cycle detection works (see Cyclic Sequence Detection). The fixed gap is the same idea as keeping the last N events of a stream you can't index backward.

Which tool for which job?

You needUse
The middle (second of two on even length)while fast and fast.next
The first middle (to split a list in half)while fast.next and fast.next.next
The n-th node from the endtwo pointers n apart, from a dummy
Remove the n-th from the endthe gap technique, stopping one node before it
The n-th from the end of a read-once streama buffer of the last n items
Detect a cyclefast/slow (see Cyclic Sequence Detection)

Common mistakes

MistakeWhat happensDo this instead
The wrong middle on even lengthsoff by one nodepick the loop condition for the middle you need
Splitting after the second middlea 2-node list splits into 2 and 0 - merge sort never endssplit after the first middle
No dummy when removing from the endremoving the head crashesstart both pointers at a dummy
Not checking n against the lengthnull dereferencevalidate n, or return the list unchanged
Expecting fewer steps than countingsame ~1.5L hopsthe win is one loop, not less work

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. On an even-length list, why does while (fast != null && fast.next != null) stop on the second middle?

Show answer

On an even-length list, fast steps past the last node to null just as slow reaches the second middle. Adding fast.next.next != null to the condition stops one round earlier, on the first middle. Pick the condition for the middle the problem wants, and check it on a four-node list.

2. Why does "remove the n-th node from the end" start both pointers at a dummy node?

Show answer

If the node to remove is the head, there's no node before it to rewire. With a dummy in front, the head has a predecessor like every other node, so the same slow.next = slow.next.next removes it too.

3. How do fast and slow find the middle without knowing the length?

Show answer

fast always covers twice the distance of slow. When fast reaches the end, after L nodes, slow has covered L/2. The relationship between the two pointers stands in for "half the length", so the number itself is never needed.

Key takeaways

  • Two pointers at different speeds, or a fixed gap apart, answer "where relative to the length" in one loop.
  • An even-length list has two middles. The loop condition decides which one you get.
  • A dummy node before the head lets you remove the head like any other node.
  • Two pointers don't save steps over counting first. They save the counting pass.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Second middlewhile fast and fast.next
First middle (splitting)while fast.next and fast.next.next
n-th from the endgap of n, from a dummy
n-th from end of a streama buffer of n items

Snippets worth knowing by heart

Java

ListNode dummy = new ListNode(0, head), fast = dummy, slow = dummy;
for (int i = 0; i <= n; i++) fast = fast.next;   // gap of n + 1
while (fast != null) { fast = fast.next; slow = slow.next; }
slow.next = slow.next.next;                      // slow is just before the target
return dummy.next;

Python

slow = fast = head
while fast and fast.next:        # second middle on even lengths
    slow, fast = slow.next, fast.next.next
return slow

The traps

  • The wrong middle: on even lengths you're off by one node. Pick the loop condition for the middle you need.
  • Splitting after the second middle: a two-node list splits into 2 and 0, so merge sort never ends. Split after the first.
  • No dummy when removing: removing the head crashes. Start both pointers at a dummy.
  • n larger than the list: the lead pointer runs off the end. Validate n first.
  • "Fewer steps": it makes about the same hops as counting first. The win is one loop, not less work.

Staff-level points, one line each

  • 1,000 nodes: second middle is 501, first is 500.
  • Fast/slow: 1,500 hops; count-then-walk: also 1,500.
  • Splitting 2 nodes after the second middle: 2 and 0 - infinite recursion.
  • No dummy, removing the head: NullPointerException; with a dummy: [2, 3].
  • Streams: deque(maxlen=n) - the 3rd from the end of 1..100 squares was 64.

Say it in an interview

  • "Fast moves two for slow's one, so slow is halfway when fast reaches the end."
  • "I keep the pointers n apart, starting at a dummy, so I stop just before the node to remove."
  • "It's one loop, not fewer steps."
  • "For merge sort I split after the first middle."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Two-Pointer List Traversal free

Remove Nth Node From End of List

MediumProCoding problemJava · Python

Delete the node that sits n places from the end of a linked list (n = 1 is the last node) and return the head, which changes when the node you delete is the first one.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Delete the Middle Node

MediumProCoding problemJava · Python

Given the head of a singly linked list of n nodes, delete its middle node and return the head. The middle node is the one at 0-indexed position n / 2 (integer division) - so for an even length it's the second of the two middles. If the list becomes empty, print EMPTY.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Two-Pointer List Traversal free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime