Linked lists are where "just use recursion, it's cleaner" stops being free advice. Almost every problem in this topic has a genuinely elegant recursive solution and a slightly less pretty iterative one — and the iterative one is usually the right answer, because a linked list's length isn't bounded the way a typical function's call stack is. This topic is as much about that judgment call as it is about the pointer manipulation itself.
Two runner techniques carry most of this topic
- Fast/slow pointers ("the runner technique"). One pointer moves one step at a time, another moves two. This single idea gives you: the middle of a list in one pass (Middle of the Linked List), whether a cycle exists at all (Linked List Cycle), and — with one extra step — exactly where that cycle begins (Linked List Cycle II). All three are the same runner, applied to a slightly different question.
- The gap technique. Advance one pointer n steps ahead of a second pointer, then move both together until the lead pointer runs out. Remove Nth Node From End of List is the clean version of this — it turns "I don't know the list's length" into a single pass instead of two.
Before coding, ask: "do I need to know the list's length, or a specific offset from the end, without a second pass?" If yes, that's the gap technique. "Do I need to find a midpoint or detect a loop without extra memory?" That's fast/slow. Recognizing which runner shape a problem wants is most of the work — the pointer bookkeeping is mechanical once you know the shape.
Recursion depth is a real cost that Big-O hides
Reverse Linked List has a famously elegant three-line recursive solution. It's also, in the most literal sense, a ticking time bomb: it recurses once per node, so a list with hundreds of thousands of nodes recurses hundreds of thousands of levels deep — and blows the call stack. Big-O notation says both the recursive and iterative versions are O(n) time — it says nothing about the fact that one of them can crash a real process on a long enough input while the other never can. This is exactly the kind of thing "efficient code" means beyond just the complexity class: the iterative three-pointer walk (prev, curr, next) does the identical work with zero growing stack.
// Elegant, and a real production risk on a long enough list --
// recurses once per node, so stack depth grows with n.
ListNode* reverse_recursive(ListNode* head) {
if (!head || !head->next) return head;
ListNode* new_head = reverse_recursive(head->next);
head->next->next = head;
head->next = nullptr;
return new_head;
}
// Same O(n) time, same result -- but O(1) stack depth, always.
ListNode* reverse_iterative(ListNode* head) {
ListNode* prev = nullptr;
while (head) {
ListNode* next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}Reverse Nodes in k-Group has the same trap in a sneakier form: the "obvious" recursive solution recurses once per group, not once per node — which sounds safe, but for a small k on a long list, n / k is still large enough to overflow the stack. The fix is the same: do it iteratively, one flat loop over the whole list.
Common mistakes
- Not using a dummy head node when the operation might need to change the head itself (Remove Nth Node From End, Merge k Sorted Lists) — without one, removing or replacing the actual first node becomes a separate special case instead of just falling out of the general logic.
- Forgetting to restore state you mutated for a check (Palindrome Linked List reverses the second half to compare it — and needs to reverse it back before returning, or the list comes back corrupted even though the answer was correct).
- Losing the "next" pointer before rewiring it — the single most common linked-list bug: writing
curr->next = prevbefore saving the originalcurr->nextin a temporary, which severs your only path to the rest of the list. - Confusing "same value" with "same node" in Copy List with Random Pointer — the whole point is that the copy's pointers must reference the *new* nodes, not accidentally alias the original list; a broken "copy" that just returns the same nodes will pass value checks but isn't a copy at all.
Takeaways
- Fast/slow pointers solve "find the middle," "detect a cycle," and "find where a cycle starts" — the same runner idea underlies all three.
- The gap technique (advance one pointer n steps first, then move both together) turns "an offset from the end" into a single pass instead of two.
- A recursive solution being O(n) time doesn't mean it's safe — recursion depth is a real, separate cost that crashes on long lists regardless of what the time complexity says. Prefer the iterative form when one exists.
- Use a dummy head whenever the head itself might need to change, and always save a node's
nextbefore you overwrite it.
Try it: the foundation, done the safe way
Reverse Linked List — get the iterative three-pointer walk automatic here, since the recursion-depth lesson above applies to several later problems in this topic too.
Loading starter code…
Try it: the runner technique
Linked List Cycle is Floyd's tortoise and hare in its purest form — the same idea reappears (with one extra step) in Linked List Cycle II later in this set.
Loading starter code…
Try it: the gap technique
Remove Nth Node From End of List turns "I don't know the length" into a single pass — and the dummy-head trick here is worth internalizing before Merge k Sorted Lists later in the set.
Loading starter code…
That's 3 of the 12 problems in this topic. Merge k Sorted Lists is worth doing once these three feel automatic — it's the one place in this topic complexity is graded on more than just correctness. See the full Linked List set →
Checkpoint · Linked List
5 questions · pass at 70%
Finished Linked List?
Pass the quiz to complete it automatically.