Python · medium · Reported at Meta
Reverse a linked list.
Asked in ML Engineer interviews, in the Python round.
Short answer
Iterate with prev/curr pointers, reversing next at each step.
How to answer it
Definition
Walk the list once, pointing each node's next at the node behind it. Three pointers, no extra memory, and the whole difficulty is not losing the rest of the list when you overwrite a pointer.
Example
class Node:
def __init__(self, val, nxt=None):
self.val, self.next = val, nxt
def reverse(head):
prev = None
curr = head
while curr:
nxt = curr.next # save it BEFORE overwriting
curr.next = prev # flip the pointer backwards
prev = curr # advance prev
curr = nxt # advance curr
return prev # prev is the new head
Output
>>> head = Node(1, Node(2, Node(3)))
>>> node = reverse(head)
>>> while node: print(node.val, end=" "); node = node.next
3 2 1
Walking one step
start None 1 -> 2 -> 3 -> None
^curr
after step 1 None <- 1 2 -> 3 -> None
^prev ^curr
after step 2 None <- 1 <- 2 3 -> None
^prev ^curr
The saved nxt is the only thing keeping the tail reachable once curr.next is overwritten. Drop that line and the list is gone after the first iteration.
Complexity
Time O(n) one pass
Space O(1) three pointers, nothing allocated
Key points
- Save
curr.nextbefore reassigning it. That single line is the whole bug surface. - Return
prev, notcurr. When the loop endscurris None andprevis the new head. - The empty list and the single node both work with no special casing, which is worth pointing out.
- The recursive version is elegant and uses O(n) stack, so it can blow up on a long list.
FAQ
- Can it be done recursively? Yes: reverse the rest, then point
head.next.nextback athead. Mention the stack depth. - What about reversing in groups of k? The same pointer dance inside a loop that tracks group boundaries. It is the natural follow-up.
- Should I draw it? Yes. Three boxes and arrows makes the pointer swap obvious to both of you.
The 30-second answer
Three pointers: previous, current, and a temporary for the next node. I walk the list once, and at each step I save the next node, point the current node's next at previous, then move previous and current forward one. When current is None the list is exhausted and previous is the new head, so I return that.
The line that matters is saving the next pointer before overwriting it, because the moment I set current dot next to previous, the rest of the list would otherwise be unreachable. It is one pass and constant extra space, and the empty list and single-node cases both fall out with no special handling.
In the interview
- What they are checking: the pointer discipline, and whether you say the edge cases.
- Common mistake: forgetting to save
curr.nextbefore overwriting it, which drops everything after the first node.