A singly linked list is a chain of nodes where each node holds a val and a next pointer to the following node. Reversing one is a classic interview warm-up — it shows up in real code any time you need to walk a chain backward (undo stacks, history traversal, parsing). See MDN on data structures for the general idea, though linked lists aren't built into JavaScript.
Implement linkedListReversal(head). Given the head of a singly linked list, reverse the direction of every next pointer and return the new head (which is the old tail). Do it in place — don't allocate new nodes.
// Each node has the shape:
// { val: any, next: Node | null }
function linkedListReversal(head) {
// returns the new head of the reversed list, or null if head is null
}
// Input: 1 -> 2 -> 3 -> 4 -> null
// Output: 4 -> 3 -> 2 -> 1 -> null
linkedListReversal(buildList([1, 2, 3, 4]));
// Input: 42 -> null (single node)
// Output: 42 -> null (same node, same value)
linkedListReversal(buildList([42]));
// Input: null (empty list)
// Output: null
linkedListReversal(null);
next must end up as null.head is null) and a single-node list must both return safely. No throws.null. You don't need to detect loops.Node class — the test harness builds plain { val, next } objects.You'll walk the list once and flip each node's next pointer to face the other way, keeping three references so you never lose the rest of the chain.
A singly linked list is a chain: each node holds a value and a single next pointer to the following node. The last node points at null. Reversing means the chain now runs the other direction — the old tail becomes the new head, the old head becomes the new tail, and every arrow between them turns around. You have to do this without making new nodes; you mutate the existing ones in place.
The catch is that each node only knows where it's going, not where it came from. The moment you flip a node's next to point backward, you've lost your only handle on the rest of the list — unless you saved it first.
Picture the list as four boxes connected by arrows. Reversing it doesn't move the boxes; it only rotates the arrows. The leftmost box (the old head) loses its forward arrow and gains a null. The rightmost box (the old tail) becomes the new entry point.
To actually perform the flip one node at a time, you need three pointers in flight:
prev — the head of the reversed chain built so far. Starts as null (nothing reversed yet).current — the node you're about to flip. Starts as the original head.next — a temporary handle on current.next before you overwrite it, so the rest of the list survives.A reasonable first try is to walk the list with just two pointers — the current node and a moving prev — and flip as you go:
function linkedListReversalBroken(head) {
let prev = null;
let current = head;
while (current !== null) {
current.next = prev; // flip this node's pointer
prev = current; // advance prev
current = current.next; // advance current — but we just overwrote current.next!
}
return prev;
}
This almost works. The flip is right, the start state is right, the return is right. The bug is on the last line of the loop: current = current.next runs after we set current.next = prev, so it advances current backward into the chain we just built, not forward into the rest of the list. On a list 1 → 2 → 3, the first iteration flips node 1's next to null, then sets current to null instead of node 2 — the loop exits after a single step and you return 1 → null. The fix: save current.next before overwriting it.
This is the classic pitfall — the logic looks right on the surface, but the mutation of current.next silently breaks the iteration. Each statement is individually correct; it's only the order that's wrong, and the bug doesn't show up until you trace what current.next actually points to after the flip.
Add a fourth line that captures the next node before the flip clobbers it:
function linkedListReversal(head) {
// `prev` is the head of the reversed chain we've built so far.
// It starts at null because we've reversed nothing yet — which is
// also the correct final `next` value for what will become the new tail.
let prev = null;
// `current` is the node we're about to flip.
let current = head;
while (current !== null) {
// Save the rest of the list BEFORE we overwrite current.next.
// This is the line the naive version was missing — if you skip
// this save, you're about to clobber your only reference to
// every node after `current`.
const next = current.next;
// Flip: this node now points back at the chain we've built.
current.next = prev;
// Advance both pointers one step forward through the ORIGINAL list.
// `prev` becomes the node we just flipped; `current` becomes the
// next un-flipped node (which is why we saved `next` above).
prev = current;
current = next;
}
// When the loop ends, `current` is null (we walked off the end) and
// `prev` is the last node we flipped — i.e. the old tail, now the head.
return prev;
}
module.exports = { linkedListReversal };
The whole shift from the naive version is one extra variable. By caching current.next into next before the flip, we hold onto the rest of the list while the assignment current.next = prev rewrites the pointer. Then current = next advances into the original chain instead of into the reversed one. The loop ends naturally when we've walked off the tail (current === null), and prev is sitting on the new head.
A few things worth pointing at:
prev = null is not a sentinel — it's the real value the new tail's next should hold. The list ends in null; the reversed list also ends in null. Starting prev at null makes the very first flip correctly terminate the new chain.current !== null, not current.next !== null. Using current.next !== null would stop one node early — the original tail would never get flipped, so the new head would be the original second-to-last node. Walk all the way off the end.val. Only the next fields mutate.Take linkedListReversal(buildList([1, 2, 3])). The chain starts as 1 → 2 → 3 → null.
Initial state: prev = null, current = node(1).
Iteration 1. current is node 1, which is not null — enter the body.
next = current.next → next now holds node 2.current.next = prev → node 1's next is now null.prev = current → prev is now node 1.current = next → current is now node 2.State after iter 1: prev = node(1) → null, current = node(2) → node(3) → null.
Iteration 2. current is node 2 — enter the body.
next = current.next → next is node 3.current.next = prev → node 2's next is now node 1.prev = current → prev is now node 2.current = next → current is now node 3.State after iter 2: prev = node(2) → node(1) → null, current = node(3) → null.
Iteration 3. current is node 3 — enter the body.
next = current.next → next is null.current.next = prev → node 3's next is now node 2.prev = current → prev is now node 3.current = next → current is null.State after iter 3: prev = node(3) → node(2) → node(1) → null, current = null.
The loop condition current !== null is now false, so we exit and return prev, which is node 3 — the new head.
For the null head case, current = head = null and the loop body never runs; we return prev, which is still null. For the single-node case, the body runs exactly once: we save next = null, flip current.next from null to null (no-op visually, but the assignment runs), advance prev to the only node and current to null. We return the node unchanged.
current.next before the flip — see the first attempt. On input 1 → 2 → 3, you'd return node(1) alone because current = current.next runs after the flip and reads null instead of node 2.current.next !== null instead of current !== null — the loop stops one node short. On 1 → 2 → 3, you'd flip nodes 1 and 2 but never flip node 3, so node 3 still points at null (good) but the function returns prev = node(2), giving you 2 → 1 → null and dropping the original tail value entirely.current instead of prev — at loop exit, current is null, so this returns null for every non-empty input. The new head is whatever prev ended on. Concrete: linkedListReversal(buildList([1, 2])) would return null instead of node(2).prev to head instead of null — the first flip would set head.next = head, creating a one-node cycle. Walking the result would loop forever. prev must start as null so the new tail's next is null.new Node(current.val) anywhere, step back and reuse the existing nodes.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
A singly linked list is a chain of nodes where each node holds a val and a next pointer to the following node. Reversing one is a classic interview warm-up — it shows up in real code any time you need to walk a chain backward (undo stacks, history traversal, parsing). See MDN on data structures for the general idea, though linked lists aren't built into JavaScript.
Implement linkedListReversal(head). Given the head of a singly linked list, reverse the direction of every next pointer and return the new head (which is the old tail). Do it in place — don't allocate new nodes.
// Each node has the shape:
// { val: any, next: Node | null }
function linkedListReversal(head) {
// returns the new head of the reversed list, or null if head is null
}
// Input: 1 -> 2 -> 3 -> 4 -> null
// Output: 4 -> 3 -> 2 -> 1 -> null
linkedListReversal(buildList([1, 2, 3, 4]));
// Input: 42 -> null (single node)
// Output: 42 -> null (same node, same value)
linkedListReversal(buildList([42]));
// Input: null (empty list)
// Output: null
linkedListReversal(null);
next must end up as null.head is null) and a single-node list must both return safely. No throws.null. You don't need to detect loops.Node class — the test harness builds plain { val, next } objects.You'll walk the list once and flip each node's next pointer to face the other way, keeping three references so you never lose the rest of the chain.
A singly linked list is a chain: each node holds a value and a single next pointer to the following node. The last node points at null. Reversing means the chain now runs the other direction — the old tail becomes the new head, the old head becomes the new tail, and every arrow between them turns around. You have to do this without making new nodes; you mutate the existing ones in place.
The catch is that each node only knows where it's going, not where it came from. The moment you flip a node's next to point backward, you've lost your only handle on the rest of the list — unless you saved it first.
Picture the list as four boxes connected by arrows. Reversing it doesn't move the boxes; it only rotates the arrows. The leftmost box (the old head) loses its forward arrow and gains a null. The rightmost box (the old tail) becomes the new entry point.
To actually perform the flip one node at a time, you need three pointers in flight:
prev — the head of the reversed chain built so far. Starts as null (nothing reversed yet).current — the node you're about to flip. Starts as the original head.next — a temporary handle on current.next before you overwrite it, so the rest of the list survives.A reasonable first try is to walk the list with just two pointers — the current node and a moving prev — and flip as you go:
function linkedListReversalBroken(head) {
let prev = null;
let current = head;
while (current !== null) {
current.next = prev; // flip this node's pointer
prev = current; // advance prev
current = current.next; // advance current — but we just overwrote current.next!
}
return prev;
}
This almost works. The flip is right, the start state is right, the return is right. The bug is on the last line of the loop: current = current.next runs after we set current.next = prev, so it advances current backward into the chain we just built, not forward into the rest of the list. On a list 1 → 2 → 3, the first iteration flips node 1's next to null, then sets current to null instead of node 2 — the loop exits after a single step and you return 1 → null. The fix: save current.next before overwriting it.
This is the classic pitfall — the logic looks right on the surface, but the mutation of current.next silently breaks the iteration. Each statement is individually correct; it's only the order that's wrong, and the bug doesn't show up until you trace what current.next actually points to after the flip.
Add a fourth line that captures the next node before the flip clobbers it:
function linkedListReversal(head) {
// `prev` is the head of the reversed chain we've built so far.
// It starts at null because we've reversed nothing yet — which is
// also the correct final `next` value for what will become the new tail.
let prev = null;
// `current` is the node we're about to flip.
let current = head;
while (current !== null) {
// Save the rest of the list BEFORE we overwrite current.next.
// This is the line the naive version was missing — if you skip
// this save, you're about to clobber your only reference to
// every node after `current`.
const next = current.next;
// Flip: this node now points back at the chain we've built.
current.next = prev;
// Advance both pointers one step forward through the ORIGINAL list.
// `prev` becomes the node we just flipped; `current` becomes the
// next un-flipped node (which is why we saved `next` above).
prev = current;
current = next;
}
// When the loop ends, `current` is null (we walked off the end) and
// `prev` is the last node we flipped — i.e. the old tail, now the head.
return prev;
}
module.exports = { linkedListReversal };
The whole shift from the naive version is one extra variable. By caching current.next into next before the flip, we hold onto the rest of the list while the assignment current.next = prev rewrites the pointer. Then current = next advances into the original chain instead of into the reversed one. The loop ends naturally when we've walked off the tail (current === null), and prev is sitting on the new head.
A few things worth pointing at:
prev = null is not a sentinel — it's the real value the new tail's next should hold. The list ends in null; the reversed list also ends in null. Starting prev at null makes the very first flip correctly terminate the new chain.current !== null, not current.next !== null. Using current.next !== null would stop one node early — the original tail would never get flipped, so the new head would be the original second-to-last node. Walk all the way off the end.val. Only the next fields mutate.Take linkedListReversal(buildList([1, 2, 3])). The chain starts as 1 → 2 → 3 → null.
Initial state: prev = null, current = node(1).
Iteration 1. current is node 1, which is not null — enter the body.
next = current.next → next now holds node 2.current.next = prev → node 1's next is now null.prev = current → prev is now node 1.current = next → current is now node 2.State after iter 1: prev = node(1) → null, current = node(2) → node(3) → null.
Iteration 2. current is node 2 — enter the body.
next = current.next → next is node 3.current.next = prev → node 2's next is now node 1.prev = current → prev is now node 2.current = next → current is now node 3.State after iter 2: prev = node(2) → node(1) → null, current = node(3) → null.
Iteration 3. current is node 3 — enter the body.
next = current.next → next is null.current.next = prev → node 3's next is now node 2.prev = current → prev is now node 3.current = next → current is null.State after iter 3: prev = node(3) → node(2) → node(1) → null, current = null.
The loop condition current !== null is now false, so we exit and return prev, which is node 3 — the new head.
For the null head case, current = head = null and the loop body never runs; we return prev, which is still null. For the single-node case, the body runs exactly once: we save next = null, flip current.next from null to null (no-op visually, but the assignment runs), advance prev to the only node and current to null. We return the node unchanged.
current.next before the flip — see the first attempt. On input 1 → 2 → 3, you'd return node(1) alone because current = current.next runs after the flip and reads null instead of node 2.current.next !== null instead of current !== null — the loop stops one node short. On 1 → 2 → 3, you'd flip nodes 1 and 2 but never flip node 3, so node 3 still points at null (good) but the function returns prev = node(2), giving you 2 → 1 → null and dropping the original tail value entirely.current instead of prev — at loop exit, current is null, so this returns null for every non-empty input. The new head is whatever prev ended on. Concrete: linkedListReversal(buildList([1, 2])) would return null instead of node(2).prev to head instead of null — the first flip would set head.next = head, creating a one-node cycle. Walking the result would loop forever. prev must start as null so the new tail's next is null.new Node(current.val) anywhere, step back and reuse the existing nodes.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.