Three pointers walking in single file. If pointer manipulation makes your brain slide off, this is the problem that finally makes it stick.
Start the walkthrough →Eleven months. 312 applications. Nine phone screens. Three of those nine screens — Cisco, an Oracle contractor role, and one bank that shall remain a bank — opened with some version of "reverse a linked list." The first time, I froze for four minutes of a forty-five minute call and never recovered. So this is reverse linked list explained for beginners, written the way I needed it explained when I was the person freezing.
This problem gets asked at 28+ companies and it is rated Easy, which is a word that does a lot of damage. Easy means "short to write once you see it." It does not mean "obvious the first time." Those are different things, and the gap between them is where people decide they aren't smart enough for this industry. They usually are. They've never been shown the mechanism.
You're handed the front of a chain. Each link holds a value and a pointer to the next link. The last link points at nothing. Your job: hand back the front of a chain that runs the other direction.
Given 1 -> 2 -> 3 -> None, return 3 -> 2 -> 1 -> None.
Here's the trap in how it's worded. "Reverse" makes you picture the whole list turning around at once, like a bus doing a three-point turn. That mental picture is unusable — you can't hold a whole list in your head and rotate it. The operation you actually perform is much smaller and happens one link at a time, and until you swap the picture, no amount of staring helps.
A linked list is not an array. There's no index. Nothing is stored next to anything else. What you have is a node — an object with two fields:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next # a reference to another node, or None
So 1 -> 2 -> 3 is three separate objects scattered anywhere in memory, and the only thing holding them together is that node 1's next field points at node 2, and node 2's next points at node 3. Node 3's next is None, which is how you know the list ended.
The critical consequence: you can only move forward, and you can only see one node at a time. If you're standing on node 2, node 1 is unreachable. It's gone. There's no back button. Everything about this problem follows from that one constraint.
When I first saw this, my instinct was to build a new list. Walk to the last node, grab it, attach it to my new list. Walk to the second-to-last node, grab it, attach it. Repeat.
That works, and it is very slow. Because you can only move forward from the head, finding the last node means walking all n nodes. Finding the second-to-last means walking n-1. Then n-2. You end up doing roughly n²/2 steps. For a list of 100,000 nodes, that's about five billion pointer hops. A judge that allows a second of runtime will cut you off somewhere around node 3,000.
The other instinct is: copy every value into an array, reverse the array, then write the values back into the nodes. That's O(n) time, which passes. But it uses O(n) extra space, and more importantly it dodges the thing the question exists to test. The interviewer is not curious whether you can reverse an array. They want to watch you rewire pointers without dropping anything. Swapping values sidesteps the entire skill. In real systems, nodes carry payloads you don't own and can't safely copy — the pointers are the structure, and the structure is what you're being asked to change.
Stop thinking about reversing a list. Think about flipping one arrow.
In 1 -> 2 -> 3 -> None, the arrow from 1 to 2 needs to become an arrow from 2 to 1. That's it. That's the whole operation, repeated.
Now the problem: the instant you point node 1's next at whatever came before it, you have destroyed the only reference to node 2. The rest of the list floats away and you cannot get it back. So before you flip anything, you save the next node in a temporary variable.
That gives you three pointers walking forward in single file:
prev — the node behind you, the front of the part you've already reversedcurr — the node you're standing on, whose arrow you're about to flipnext_node — the node ahead, saved so you don't lose the rest of the chainWalk 1 -> 2 -> 3 -> None by hand. Start with prev = None and curr = node 1.
| Pass | save next_node |
flip curr.next = prev |
then prev |
then curr |
reversed so far |
|---|---|---|---|---|---|
| 1 | node 2 | 1 -> None | node 1 | node 2 | 1 -> None |
| 2 | node 3 | 2 -> 1 | node 2 | node 3 | 2 -> 1 -> None |
| 3 | None | 3 -> 2 | node 3 | None | 3 -> 2 -> 1 -> None |
curr is now None, so the walk is over. prev is sitting on node 3 — the new head. Notice that prev starting at None is what makes the old head point at nothing, which is exactly right: the old first node has to become the new last node.
def reverseList(head):
prev = None # nothing comes before the first node yet
curr = head # start standing on the front of the list
while curr is not None:
next_node = curr.next # 1. save the rest of the list before we break the link
curr.next = prev # 2. flip this one arrow backwards
prev = curr # 3. prev steps forward onto the node we finished
curr = next_node # 4. curr steps forward into the saved remainder
return prev # curr fell off the end; prev is the new head
In plain English: those four lines inside the loop always run in that order, and the order is the whole problem. Save, flip, advance, advance. Line 1 protects the future. Line 2 does the actual work. Lines 3 and 4 move both pointers one step down the chain. When curr walks off the end of the list, prev is standing on what used to be the last node, which is now the first.
Time: O(n) — every node is touched once. Space: O(1) — three variables, no matter how long the list is.
The recursive version exists and is shorter:
def reverseList(head):
if head is None or head.next is None:
return head # empty list, or the last node: that's the new head
new_head = reverseList(head.next) # reverse everything after me
head.next.next = head # the node after me now points back at me
head.next = None # and I point at nothing (for now)
return new_head
It's elegant and it's O(n) time, but it's O(n) stack space — a million-node list will blow the stack. Lead with the iterative one in an interview and mention the recursive one as an alternative. That sequencing is itself a signal.
Overwriting curr.next before saving it. You write curr.next = prev first, then reach for curr.next to advance — and it now points backward, so you walk into the part you already reversed and loop forever. Save next_node first, always.
Returning curr instead of prev. The loop only exits when curr is None. Returning it hands back an empty list and the tests fail in a way that looks mysterious for about ten minutes.
Initializing prev = head instead of None. Then the old head points at itself and you've built a cycle. The old head has to end up pointing at nothing.
Forgetting the empty list. If head is None, the loop body never runs and prev is still None, which is the correct answer. The iterative version handles it for free — but say that out loud rather than hoping nobody asks.
Reaching for recursion first. It reads as showing off, and then the follow-up question about memory usage arrives and you're defending a choice you didn't need to make.
What I can't tell you is whether any of this measures what it's supposed to. Being able to flip pointers under time pressure and being able to build software people rely on are not obviously the same skill, and nobody has published the study that settles it. So learn the mechanism until your hand moves without you — and keep asking why a company that will never ask you to hand-reverse a chain of nodes decided this was the thing that separates the hires from the rejections.
Losing the rest of the list by overwriting `curr.next` before saving it — save `next` first, always.
Returning `curr` (now None) instead of `prev` at the end.
Initializing `prev` to the head instead of None — the old head must become the tail pointing at nothing.
Reaching for recursion first in an interview — it's elegant but O(n) stack; lead with iterative.