Linked list problems are rarely hard because of the algorithm. What trips people up is pointer management: keeping track of prev, curr and next without breaking the chain. Two habits make most of them routine. Put a dummy node in front of the head, and change pointers in an order that never loses the rest of the list.
The idea#
Deleting or inserting a node means changing the next of the node before it. The head has no node before it. So code that handles "delete a node" usually needs a separate branch for "delete the head". A dummy node removes that special case:
- Create
dummy = ListNode(0, head). Now every real node, the head included, has a predecessor. - Do all the work starting from
dummy. - Return
dummy.next. It is the new head, whatever happened to the old one.
With linked lists, always think of a dummy node. It costs one extra node and removes a whole class of bugs.
The second habit is about order. When you rewire, follow one rule: before you overwrite a next pointer, make sure something else still points to the node it pointed at. Save it in a variable or link it in first.
How to spot it#
These signals point to a dummy node.
- The head itself might be deleted, moved or replaced.
- "Remove all nodes with value
val", "remove duplicates", "partition", "swap pairs". - You build a new list node by node. A dummy gives you a fixed place to start and a tail to append to.
- There is more than one output list: split into two chains, then join them.
Template#
This is the delete-while-walking shape. prev moves forward only when you keep the current node.
dummy = new node whose next is head # head gets a predecessor
prev = dummy
curr = head
while curr is not None:
if curr should be removed:
prev.next = curr.next # unlink curr; prev stays put
else:
prev = curr # keep curr, so it becomes prev
curr = curr.next
return dummy.next # the real head, even if it changed
The dummy helps when you build a new list, too. To build a list in order while you walk something else, use a dummy plus a tail pointer: tail.next = node, then tail = node.
Inserting right after the dummy builds a list in reverse order: node.next = dummy.next, then dummy.next = node. You get the reversal for free.
Rewiring in a safe order#
The template changes one pointer per step. Swaps and partial reversals change several at once, and that's where the rewiring rule from above matters most. Order the writes so no node becomes unreachable:
- Point the node before the block at its new first node.
- Let the node that will end up last grab the rest of the list.
- Close the links inside the block.
- Move
prevto the new last node of the block.
Step 2 must come before any write that overwrites the only pointer to the rest of the list.
Pitfalls#
These mistakes break the chain most often.
- Returning
headinstead ofdummy.next. If the head was deleted,headpoints to a detached node. - Advancing
prevafter a deletion. When you unlinkcurr,prevstays where it is. The next node might need deleting too. - Overwriting
nexttoo early. Savecurr.next(or link it elsewhere) before you change it. - Leaving a dangling tail. When you split or reorder nodes in place, set the last node's
nexttoNone. Otherwise it may still point into the old list and create a cycle. - Ignoring the constraints. Unusual constraints (unique values, a node that is never the tail) often allow a trick that the general case does not.
Worth exploring: in-place reversal (Reverse Linked List, LC 206). It is the prev / curr / next rewiring loop that Reverse Linked List II, Palindrome Linked List and Reorder List all build on.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first two.
- 203. Remove Linked List Elements
- 83. Remove Duplicates from Sorted List
- 206. Reverse Linked List
- 237. Delete Node in a Linked List
- 24. Swap Nodes in Pairs
- 86. Partition List
- 445. Add Two Numbers II
- 92. Reverse Linked List II
- 2. Add Two Numbers
- 25. Reverse Nodes in k-Group
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Remove Linked List Elements
This is the template as written. The head itself may hold the value, so start from a dummy. Move prev only when you keep curr.
Hint: Remove Duplicates from Sorted List
Compare each node with the last node you kept. Give the dummy a value outside the allowed range (for example -101 when values are in [-100, 100]). Then the check curr.val == prev.val works from the very first node.
Hint: Delete Node in a Linked List
You get only the node to delete, so you cannot reach its predecessor. Copy the next node's value into this node, then unlink the next node. This works only because values are unique and the node is never the tail.
Hint: Swap Nodes in Pairs
Keep a prev before each pair first -> second. Set prev.next = second, then first.next = second.next, then second.next = first. If second.next is overwritten before first grabs it, the rest of the list is gone. After the swap, first is the new prev.
Hint: Partition List
Walk once and append each node to one of two chains, less and rest, each with its own dummy and tail. Link less_tail.next = rest_dummy.next at the end. If you reuse the original nodes, set rest_tail.next = None, or you may create a cycle.
Hint: Add Two Numbers II
The digits come most significant first, but addition starts from the least significant. Push both lists' values onto stacks, then pop and add with a carry. Insert each new digit right after the dummy, so the result comes out in the right order with no reversal step.
Hint: Reverse Linked List II
Use a dummy, because the reversed block may start at the head. Walk prev to the node just before position left. Then run the in-place reversal loop on the block and reconnect both ends.
Hint: Reverse Linked List
Walk with prev and curr. Save curr.next, point curr.next back at prev, then step both forward. When curr runs out, prev is the new head.
Hint: Add Two Numbers
The digits are stored least significant first, so add while you walk both lists, carrying as you go, and append to a dummy head. Keep going while either list or the carry remains.
Hint: Reverse Nodes in k-Group
Before reversing a group, check that k nodes remain. If not, leave them as they are. Reverse the k nodes in place, then connect the previous group's tail to the new group head. A dummy node keeps the first group from being a special case.
Key takeaways#
- With linked lists, always think of a dummy node: the head gets a predecessor and
dummy.nextis the answer. - Change the predecessor's
nextto delete; advanceprevonly when you keep a node. - Save or relink
nextbefore you overwrite it. - A dummy plus a tail pointer is the standard way to build a list.
- Inserting after the dummy builds a list in reverse for free.