null
prev
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.
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.
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.prev = null, curr = head. The reversed list starts empty (null).next = curr.next before overwriting — otherwise you lose the rest of the list.curr.next = prev — the current node now points backward into the already-reversed prefix.prev = curr, then curr = next. Move the window one step right.curr = null, prev is the new head.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.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).
prev = null, curr = head (1). We will rewire each next pointer to point backward.// 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;
}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.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.curr.next = prev redirects the current node backward into the already-reversed prefix. This is the core mutation — one arrow flipped per iteration.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.curr hits null, prev points to what was the last node — the new head. The loop naturally terminates because curr advances each iteration.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.head.next.next = head and head.next = null. Mention the O(n) stack cost; the iterative version is strictly better on space.m - 1, apply the same three-pointer flip for exactly n - m + 1 steps, then splice the reversed segment back in.| "reverse a linked list" or "flip next pointers" | three-pointer: prev / curr / next |
| "in-place" linked list mutation | save 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 |
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;prev (already-reversed tail), curr (current node), next (saved successor before overwrite).[1, 2, 3]. After the first full iteration (save + flip + advance), what are prev and curr?reverseListRecursive(head.next) returns, two operations are needed. What are they?