HARD Lab · Linked List · 53

206. Reverse Linked List

Given the head of a singly linked list, reverse it in place using three pointers — prev, curr, and next — so each node's next arrow flips to point backward in a single O(n) pass.

EasyLinked ListPointer RewiringTypeScript

PROBLEM What we're solving

Given the head of a singly linked list, return the head of the reversed list. For example, 1 → 2 → 3 → 4 → 5 → null becomes 5 → 4 → 3 → 2 → 1 → null. No new nodes; just change the next pointers. Appears everywhere: reversing sublists, palindrome checks, reordering linked lists.

KEY IDEA One pointer trailing, one leading — flip as you go

Insight → at each node, you only need to redirect curr.next from curr's successor to prev. But doing so severs the forward chain, so you must save next = curr.next before the rewire. Three pointer variables — prev, curr, next — are all you need. Walk forward, flip backward.

RECIPE Save, flip, advance, repeat

  • 0 · Initialize. prev = null, curr = head. The reversed list starts empty (null).
  • 1 · Save next. next = curr.next before overwriting — otherwise you lose the rest of the list.
  • 2 · Flip pointer. curr.next = prev — the current node now points backward into the already-reversed prefix.
  • 3 · Advance both pointers. prev = curr, then curr = next. Move the window one step right.
  • 4 · Return. When curr = null, prev is the new head.
Classic confusion → the order of the advance matters: prev = curr must come before curr = next, and both must come after the flip. Swapping any two of these three lines drops or corrupts nodes. Memorize the invariant: save → flip → advance.
Pattern transfer → the same three-pointer dance powers Reverse Linked List II (reverse a sublist, splice back), Palindrome Linked List (reverse the second half in-place), Reorder List (split + reverse + merge), and Reverse Nodes in k-Group. Nail this pattern once and those all follow.

COST Complexity & alternatives

Copy to array, build backwards
O(n) extra
Correct but uses O(n) space.
Three-pointer in-place
O(1) space
O(n) time, O(1) space — optimal.

The recursive approach is elegant but costs O(n) call-stack space, making it worse in practice for large lists despite the same wall-clock time. Interviewers often ask you to name the space trade-off — recursive is not O(1).

RUN IT Rewire each next pointer to point backward

step 0 / 16
STARTStart: prev = null, curr = head (1). We will rewire each next pointer to point backward.
list →1curr2345
State
null
prev
1
curr
null
next
prev pointercurr pointernext pointer
slowfast

TYPESCRIPT The solution, annotated

reverseList.ts
// Definition for singly-linked list.
// class ListNode {
//   val: number;
//   next: ListNode | null;
//   constructor(val?: number, next?: ListNode | null) {
//     this.val = val ?? 0;
//     this.next = next ?? null;
//   }
// }

function reverseList(head: ListNode | null): ListNode | null {
  let prev: ListNode | null = null;
  let curr: ListNode | null = head;

  while (curr !== null) {
    const next = curr.next;   // save: we're about to overwrite it
    curr.next = prev;         // rewire: point backward
    prev = curr;              // advance prev
    curr = next;              // advance curr
  }

  return prev;                // prev is the new head
}

// ─── Recursive variant ───────────────────────────────────────────────────────
function reverseListRecursive(head: ListNode | null): ListNode | null {
  if (head === null || head.next === null) return head; // base: 0 or 1 node

  const newHead = reverseListRecursive(head.next);      // recurse to tail
  head.next.next = head;                                // child points back
  head.next = null;                                     // cut forward link
  return newHead;
}

Reading it block by block

Lines 14–15 — initialize. prev starts as null (the tail of the future reversed list), and curr starts at head. Together they track the boundary between reversed and not-yet-reversed.
Line 18 — save next. const next = curr.next captures the successor before we overwrite curr.next on the next line. This is the step beginners forget, causing the rest of the list to be lost.
Line 19 — flip the arrow. curr.next = prev redirects the current node backward into the already-reversed prefix. This is the core mutation — one arrow flipped per iteration.
Lines 20–21 — advance. prev = curr extends the reversed prefix by one node; curr = next moves into the remaining unreversed list. Order matters: prev must be set before curr is overwritten.
Line 24 — return prev. When curr hits null, prev points to what was the last node — the new head. The loop naturally terminates because curr advances each iteration.
Lines 27–35 — recursive variant. Base case: 0 or 1 nodes, return as-is. Otherwise recurse to the tail, then do two pointer operations: head.next.next = head (child points back to parent) and head.next = null (sever the old forward link). The new head bubbles up unchanged from the deepest call. Space cost: O(n) call stack.
Complexity → O(n) time — one pass through the list. O(1) space for the iterative version (three pointers regardless of list length). The recursive version is also O(n) time but costs O(n) call-stack space due to the recursion depth.

INTERVIEWFollow-ups they'll ask

  • "Can you do it recursively?" Yes — recurse to the tail, then set head.next.next = head and head.next = null. Mention the O(n) stack cost; the iterative version is strictly better on space.
  • "Reverse only nodes m through n (Reverse Linked List II)?" Walk to node m - 1, apply the same three-pointer flip for exactly n - m + 1 steps, then splice the reversed segment back in.
  • "How would you check if a linked list is a palindrome?" Find the midpoint with slow/fast pointers, reverse the second half in-place, compare front and back halves, then optionally restore.
  • "Reverse in groups of k (Reverse Nodes in k-Group)?"Verify at least k nodes remain, reverse that group with the same loop, then recurse/iterate for the rest and link the group tail to the next group's head.
  • "What if the list has a cycle?"The iterative reversal would loop forever. Detect the cycle first (Floyd's algorithm) and handle or reject it before attempting reversal.

MNEMONIC The one-liner

"Save the future, flip the arrow, then shuffle forward — prev is your new head."

TRIGGERS When you see ___ → reach for ___

"reverse a linked list" or "flip next pointers"three-pointer: prev / curr / next
"in-place" linked list mutationsave next before overwriting curr.next
"palindrome linked list" or "reorder list"reverse second half with same pattern
"reverse nodes in k-group"apply reversal loop k times, splice, repeat

SKELETON The reusable shape

skeleton.ts
let prev: ListNode | null = null;
let curr: ListNode | null = head;

while (curr !== null) {
  const next = curr.next;
  curr.next = prev;
  prev = curr;
  curr = next;
}

return prev;

FLASHCARDS Tap to flip

What three pointers does the iterative reversal use?
prev (already-reversed tail), curr (current node), next (saved successor before overwrite).
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
Time and space complexity of the iterative reversal?
QUESTION 02
Why must you save next = curr.next before doing curr.next = prev?
QUESTION 03
Trace input [1, 2, 3]. After the first full iteration (save + flip + advance), what are prev and curr?
QUESTION 04
What value does prev hold when the while loop terminates, and why is that the answer?
QUESTION 05
Space complexity of the recursive reversal?
QUESTION 06
In the recursive variant, after reverseListRecursive(head.next) returns, two operations are needed. What are they?
QUESTION 07
Which problem does NOT directly use the reverse-linked-list pattern?