Reverse Linked List
Turn every next-pointer in a singly linked list around, so the last node becomes the head and the first becomes the tail.
Arrows all point right. Every one of them has to end up pointing left.
1def reverse_list(head):2 prev = None3 curr = head4 while curr:5 nxt = curr.next6 curr.next = prev7 prev = curr8 curr = nxt9 return prevRead the 10 steps as text
- 1Arrows all point right. Every one of them has to end up pointing left.
- 2At 3. Save the forward link to 1 before overwriting it.
- 3Point 3 at prev, which is null. The old head is now the tail.
- 4At 1. Save the forward link to 4 before overwriting it.
- 5Point 1 back at 3. That arrow is now reversed for good.
- 6At 4. Save the forward link to 5 before overwriting it.
- 7Point 4 back at 1. That arrow is now reversed for good.
- 8At 5. Nothing after it, so next is null โ this is the last node.
- 9Point 5 back at 4. That arrow is now reversed for good.
- 10curr ran off the end, so prev is the new head. The list now reads 5 โ 4 โ 1 โ 3.
The idea
A singly linked list can only be walked forwards, and reversing it means every node must end up pointing at the node that currently points at *it*. The catch is that once you overwrite a node's next-pointer, the rest of the list is unreachable โ you've sawn off the branch you were standing on.
So the whole problem is one of ordering: save the forward link before you destroy it. That's what the temporary `nxt` is for, and it's the only reason the loop needs three variables instead of two.
After that it's a walk. At each node you know where you came from (`prev`), where you are (`curr`), and where you were going (`nxt`). Point curr backwards, then shuffle all three one step to the right. When curr falls off the end, prev is standing on the last real node โ which is the new head.
`prev` starting at null isn't an edge case, it's the answer to 'what should the old head point to?'. The old head becomes the tail, and a tail points at null. Initialising prev to null makes the first iteration do that for free.
The approach
- 1Set prev to null and curr to the head.
- 2While curr isn't null: save curr.next into nxt.
- 3Point curr.next at prev โ this is the actual reversal.
- 4Move prev to curr and curr to nxt.
- 5When the loop ends, curr is null and prev is the new head. Return prev.
Complexity
One pass, and the only extra memory is three pointers regardless of list length. The recursive version is also O(n) time but O(n) space for the call stack, which is the trade worth naming out loud.
Code
def reverse_list(head):
prev = None
curr = head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prevWhat goes wrong
- โขWriting `curr.next = prev` before saving `curr.next`. The remainder of the list is now orphaned and the loop can't advance โ this is the mistake, and it's why the temporary exists.
- โขReturning `head` at the end. head is still the original first node, which is now the tail, so you return a one-element list. The answer is `prev`.
- โขInitialising prev to the head instead of null, which leaves a cycle between the first two nodes and hangs anything that later walks the list.
- โขBeing unable to say why the loop condition is `curr` and not `curr.next`. Stopping at `curr.next` leaves the final node unreversed โ worth stating that you considered it.