skip to content

In iterative singly-linked-list reversal, why must you save the next node before rewiring?

level: juniorimportance: must knowfreq 88%

answer

  1. count the outgoing links per node
  2. what still names the unvisited remainder?
  3. one assignment destroys a reference
  4. order of the four assignments matters
  5. save successor, flip, then advance

basics

~20 s

Rewiring the current node's outgoing link overwrites the only handle on the rest of the list. Saving the successor into a temporary reference first keeps that handle; without it, every node past the current one becomes unreachable.

solid answer

~40 s

A node in a singly linked list carries exactly one outgoing link, so `curr.next` is the only handle anyone holds on the remainder. If you write `curr.next = prev` first, that handle is gone and the unvisited tail — in a newest-first notification feed, every older record — becomes unreachable, with no way back because links only point forward. The fix is the fixed four-step body: save `nxt = curr.next`, rewire `curr.next = prev`, then advance `prev = curr` and `curr = nxt`. The empty list and the single-node list need no special case; the loop runs zero or one times and still returns the right head. The whole walk is O(n) time and O(1) auxiliary space, since those three references are the only extra memory.

code

pseudocode · 7 lines
pseudocode
prev = null
curr = head
while curr != null:
    curr.next = prev    // the only handle on the remainder is gone
    prev = curr
    curr = curr.next    // reads the link just overwritten
return prev

go deeper

for a junior

Be ready to write the four-line loop body from memory and say why the save comes first. Mention the empty and single-node inputs before you start coding — it costs one sentence and interviewers listen for it.

for a middle

Explain the failure by tracing it, not by naming it: show that dropping the save ends the loop after one node and silently returns a one-element list. Then give the O(n) time and O(1) space claim with the reason.

for a senior

Point out that the mistake fails silently rather than loudly, and that the sibling error produces a self-loop whose symptom appears in a later traversal, far from the cause. Note that reversing in place invalidates references others hold to the old head.

for a principal

Own the framing that a structure with one outgoing link per node forces destructive updates, so every manipulation needs an explicit save-before-overwrite discipline. Decide whether an in-place flip is acceptable at all when other components hold references into the same nodes.

## The constraint that creates the whole problem A node in a singly linked list holds a value and exactly **one** outgoing link, pointing at its successor. There is no backward link. That single fact generates every rule in list reversal: the moment you overwrite a node's outgoing link, whatever it used to point at is reachable only if some *other* reference still names it. Picture a notification feed stored newest-first — the head is the most recent record, and each node links to the next-older one. Replaying it oldest-first means flipping the direction of every link, in place, without building a second feed. ## The four-step body The iterative walk keeps three references: - `prev` — head of the portion already reversed. It starts as null, because the original head becomes the new tail and must end up pointing at nothing. - `curr` — the node currently being flipped. - `nxt` — a temporary holding `curr`'s *original* successor. The body, in exactly this order: ``` nxt = curr.next // save the remainder curr.next = prev // flip this node's link prev = curr // extend the reversed prefix curr = nxt // step into the remainder ``` Only the first line is defensive; the other three are bookkeeping. Reordering any of them breaks the list. ## Trace the mistake, don't just name it Suppose the save is dropped and the body is `curr.next = prev; prev = curr; curr = curr.next`. Start with `prev = null`, `curr = head`. 1. `curr.next = prev` sets the head's link to null. The second record — and through it every older record — is now named by nothing. 2. `prev = curr`, so `prev` is the head. 3. `curr = curr.next` reads the link that was just overwritten, so `curr` becomes null. The loop condition fails and the routine returns a **one-node list**. The failure is silent: no error is raised, no crash, no cycle — the feed simply arrives with one record in it. Silent truncation is exactly why interviewers use this question; a candidate who narrates the trace above has shown they can hold two references in their head at once. A close cousin of the same mistake is advancing `prev` before saving: `prev = curr; nxt = curr.next; curr.next = prev` sets `curr.next = curr`, a **self-loop**. That failure is worse than truncation, because the next traversal of the feed never terminates. ## The degenerate cases fall out for free With the loop written as `while curr != null`, an empty list never enters the body and `prev` is returned still null — the correct answer. A single-node list enters once, sets that node's link to null (it already was null), and returns it unchanged. No guard clause is needed. Say this out loud at the whiteboard before you write the loop: stating the empty and single-node cases up front is a cheap, heavily rewarded habit, and candidates who add an `if head == null` guard usually add three more unnecessary guards after it. ## Why no second list is needed Reversal is often mis-answered as "walk the list pushing values onto a stack, then rebuild." That does work and it is still O(n) time, but it costs O(n) extra space and, in the value-copying form, produces different node objects — anything else holding a reference to a record now points into the old, half-dismantled structure. In-place link flipping keeps every node identity intact and uses O(1) auxiliary space. The three references do not grow with n; that is the entire space argument. One genuine subtlety: in-place reversal *invalidates* any reference someone else holds to the old head, because that node is now the tail. If a live consumer is walking the feed concurrently, reversing under it is not safe — but that is a coordination question, not a pointer question. ## Termination and what comes back The loop advances `curr` one node per iteration and never revisits a node, so it runs exactly n times and terminates when `curr` is null. At that point `prev` names the last node visited — the original tail — which is the head of the reversed list. Return `prev`. Returning `curr` returns null, one of the most common submitted bugs in this exercise. ## The doubly linked variant If each node also stores a backward link, reversal means swapping the two links of every node and then returning the old tail. The same discipline applies for the same reason: save what you are about to overwrite before you overwrite it.

  • When the loop exits, what do you return, and why not the current pointer?
    Return `prev`. The loop ends when `curr` is null, so returning `curr` returns an empty list. `prev` names the last node visited — the original tail — which is now the head of the reversed list. This is the most common bug after the missing save.
  • How does this loop behave on an empty list and on a single-node list?
    Both work with no special case. An empty list fails the loop condition immediately and returns the initial `prev`, which is null. A single-node list runs the body once, sets that node's link to null, and returns it. Adding guard clauses for these is unnecessary code an interviewer will ask you to justify.
  • What if you swap the last two assignments and advance the current pointer before the previous one?
    Then `prev` is set from the already-advanced `curr`, so `prev` and `curr` end up naming the same node and the flip on the following iteration points a node at itself. That creates a self-loop, and any later traversal of the list never terminates — a worse outcome than truncation because it fails far from the cause.

Cutting the rope that ties you to the rest of a chain: grab the next link with your other hand first, or the chain drops and there is nothing to reach for.

saying these in an interview costs you the question

  • Rewrites the current node's link before saving its successor
  • Believes a successor can be reached backwards from the node it follows
  • Copies values into a second list instead of relinking in place
  • Adds guard clauses for empty and single-node inputs the loop already handles
  • Returns the current pointer at the end instead of the previous one

context