Linked List Interview Pointer Bugs and How to Avoid Them
Linked list problems look easy on paper: a handful of nodes and a few pointers. In a live interview they cause more silent failures than almost any other topic. You lose the rest of the list, dereference None, remove the wrong node, or loop forever. Each bug takes one line to introduce and five minutes to find.
This guide covers the most common linked list interview pointer bugs, the correct pattern for each, and a dry-run habit that catches them before your interviewer does. The examples are in Python, but the same logic applies in Java, C++, JavaScript, or Go.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
Bug 1: losing the rest of the list
The classic reversal mistake is overwriting curr.next before saving it:
# Buggy
while curr:
curr.next = prev # the rest of the list is now unreachable
prev = curr
curr = curr.next # now the old prev, not the old next
Save first, then rewire:
def reverse(head):
prev, curr = None, head
while curr:
nxt = curr.next # 1. save
curr.next = prev # 2. rewire
prev = curr # 3. advance prev
curr = nxt # 4. advance curr
return prev
Say the four steps out loud as you write them: "save, rewire, advance, advance." It's hard to get the order wrong while you're narrating it.
Bug 2: tuple assignment in the wrong order
Python's multiple assignment looks safe, but the targets are assigned left to right:
# Correct: the right side is evaluated first, then curr.next is set while curr is still the old node
curr.next, prev, curr = prev, curr, curr.next
# Buggy: curr is reassigned before curr.next, so the wrong node's pointer is changed
prev, curr, curr.next = curr, curr.next, prev
In the buggy version, curr.next = prev runs on the new curr. It rewires the wrong node, and a step later it tries to set .next on None and crashes. If you're nervous, use the explicit four-line version. Clarity beats cleverness in an interview.
Bug 3: special-casing the head instead of using a dummy
Deleting or inserting at the head needs a special case unless you add a dummy (sentinel) node:
def remove_value(head, target):
dummy = ListNode(0, head)
prev = dummy
while prev.next:
if prev.next.val == target:
prev.next = prev.next.next # don't advance; check the new next
else:
prev = prev.next
return dummy.next
Two pointer bugs are hidden here:
- Returning
headinstead ofdummy.next, which is wrong if the head was removed. - Advancing
prevafter a deletion, which skips a node when two targets are adjacent.
Say: "I'll use a dummy node so removing the head isn't a special case."
Bug 4: fast/slow null checks
Finding the middle with two pointers:
def middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow # second middle for even length
Common mistakes:
while fast.next and fast:checksfast.nextbeforefast, which crashes whenfastisNone.while fast.next.next:crashes whenfast.nextisNone.- Not knowing which middle you return for even lengths. State it, or ask which one the problem wants.
If you need the first middle (for example, to split the list in half), start fast at head.next:
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# slow is the end of the first half (assuming head is not None)
Bug 5: off-by-one in "remove nth from end"
Move fast n + 1 steps ahead of a dummy, so that slow stops before the node to remove:
def remove_nth_from_end(head, n):
dummy = ListNode(0, head)
fast = slow = dummy
for _ in range(n + 1):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
This assumes 1 ≤ n ≤ length. Say so, or ask. Starting from head instead of dummy, or moving n steps instead of n + 1, removes the wrong node or crashes when n equals the length.
Bug 6: cycle detection that crashes or never ends
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
Mistakes to avoid:
- Comparing
slow == fastbefore moving them. They start equal, so it returns True right away. - Comparing values (
slow.val == fast.val) instead of identity. Duplicate values cause false positives. - Looping with
while slowonly, which skips thefastnull checks and crashes on a list without a cycle.
Bug 7: merging without a tail pointer
def merge(a, b):
dummy = tail = ListNode()
while a and b:
if a.val <= b.val:
tail.next, a = a, a.next
else:
tail.next, b = b, b.next
tail = tail.next
tail.next = a or b # attach the leftover list
return dummy.next
The usual bugs are forgetting tail = tail.next (everything overwrites one link) and forgetting to attach the leftover nodes. Using <= keeps the merge stable.
Bug 8: accidental cycles after rewiring
When you split, reorder, or partition a list, the last node of a new list may still point into the old one:
# After building a "less than x" list and a "greater or equal" list:
greater_tail.next = None # cut the old link
less_tail.next = greater_dummy.next
Forgetting the None creates a cycle, and the interviewer's test loops forever.
The dry-run habit that catches all of these
Before you say "done," trace a tiny list by drawing it:
dummy -> 1 -> 2 -> 3 -> None
^prev ^curr
Check three inputs:
- Empty list (
head is None) - Single node
- Two or three nodes, including the case where the head itself changes
At each step, say which pointer moves and where it points. Most pointer bugs show up within the first two iterations.
Talk tracks
"I'll save
curr.nextbefore rewiring so I don't lose the rest of the list."
"A dummy node means the head isn't a special case. I'll return
dummy.next."
"The loop condition checks
fastbeforefast.nextto avoid dereferencing None."
Soft practice note
Pointer bugs are a pressure problem more than a knowledge problem. Most people can reverse a list calmly, and fewer can do it with someone watching and a clock running. If you want timed, out-loud practice sessions to make these patterns automatic, Luna Interview is built for calm, structured interview rehearsal and prep before the real round.
Pre-interview checklist
- [ ] Reversal: save, rewire, advance, advance
- [ ] Explicit lines instead of risky tuple assignment
- [ ] Dummy node for any head change; return
dummy.next - [ ] Don't advance
prevafter a deletion - [ ]
while fast and fast.next, in that order - [ ] n + 1 steps from the dummy for nth-from-end
- [ ] Identity comparison (
is) for cycle detection - [ ] Tail pointer plus leftover attach when merging
- [ ] Cut old links with
Noneafter splitting - [ ] Dry run: empty, single, two or three nodes
Linked lists reward slow, deliberate pointer moves. Narrate each one, and the bugs mostly go away.
Rehearse with an AI interview copilot, then browse commonly reported interview questions.