Browse curriculum

Reverse Linked List

Reverse a singly linked list in place by flipping one link at a time.

Problem Understanding

Reverse a linked list: given the head of a singly linked list, reverse it and return the new head.

Example: 1 -> 2 -> 3 -> 4 -> 5 becomes 5 -> 4 -> 3 -> 2 -> 1. An empty list stays empty.

Attempt 1: Copy Into an Array

Walk the list, push every value into an array, then walk the list again writing the values back in reverse order. It works in O(n) time, but it needs O(n) extra space and it reverses the values, not the list: every node keeps its next. The in-place version reverses the links themselves with O(1) extra space.

The Intuition: Flip One Link at a Time

Reversing a list means every arrow points the other way. Walk the list once and flip one arrow per node:

  1. curr is the node whose next you are about to flip.
  2. prev is the node it should point back at — the head of the part already reversed (NULL at the start).
  3. next remembers the node after curr, because the flip is about to erase that link.

After each flip, prev and curr both step forward. When curr falls off the end, prev is sitting on the old tail — the new head.

What Goes Wrong If You Don't Save next

Suppose you flip first: curr.next = prev. That line overwrites the only pointer to the rest of the list. On 1 -> 2 -> 3, flipping node 1 first leaves 1 -> NULL, and nodes 2 and 3 are now unreachable — there is no way back to them. That is why every iteration starts with next = curr.next: save the way forward, then cut it.

Interactive Walkthrough

[1, 2, 3, 4, 5]

START

old head curr

1

#0

→

2

#1

→

3

#2

→

4

#3

→

5

#4

→ NULL

prev

NULL

curr

#0 (1)

next

—

 

prev = NULL, curr = head, node 0 (1)

Next

Save next = node 1 (2) before touching node 0 (1): the flip is about to overwrite the only link to it

Values

Legend & Complexity

curr: the node whose link is being flipped

prev: the head of the part already reversed

Reversed: its next now points back toward the old head

Done: prev is the new head

Time

O(n)

Space

O(1)

Each iteration is split into its three moves: save next, flip curr's link, then advance prev and curr. While the reversal runs, the list is drawn as two chains — the reversed part ending at NULL, and the untouched rest starting at curr — held together only by the saved next.

Dry Run Table

Input: 1 -> 2 -> 3 -> 4 -> 5 (nodes #0 to #4)

Iteration curr Save next Flip Reversed so far Then
1 #0 (1) #1 #0 → NULL 1 prev = #0, curr = #1
2 #1 (2) #2 #1 → #0 2 -> 1 prev = #1, curr = #2
3 #2 (3) #3 #2 → #1 3 -> 2 -> 1 prev = #2, curr = #3
4 #3 (4) #4 #3 → #2 4 -> 3 -> 2 -> 1 prev = #3, curr = #4
5 #4 (5) NULL #4 → #3 5 -> 4 -> 3 -> 2 -> 1 prev = #4, curr = NULL

curr is NULL, so the loop ends and the function returns prev, node #4: 5 -> 4 -> 3 -> 2 -> 1.

The Recursive Version

The same flips can be done on the way back out of recursion. Reverse everything after head first; the node right after head is then the tail of that reversed part, so point it back at head and cut head's own link:

def reverseList(head):
    if head is None or head.next is None:
        return head
    new_head = reverseList(head.next)
    head.next.next = head
    head.next = None
    return new_head

On 1 -> 2 -> 3, the calls go down to node 3 (the base case, the new head). Returning, node 2's call makes 3 -> 2, then node 1's call makes 2 -> 1. Forgetting head.next = None leaves the old head pointing forward, which creates a cycle between the last two nodes. Time is O(n); space is O(n) for the call stack.

The Solution Template

Iterative in-place reversal with prev, curr and next.

Reverse Linked List Code
1var reverseList = function(head) {
2 let prev = null, curr = head;
3 while (curr) {
4 const next = curr.next;
5 curr.next = prev;
6 prev = curr;
7 curr = next;
8 }
9 return prev;
10};

Edge Cases & Common Mistakes

  • Empty list: curr starts at NULL, the loop never runs, and prev (NULL) is returned.
  • One node: one iteration points it at NULL — already its next — and it is returned as the new head.
  • Returning head instead of prev: after the loop, head is the old first node, now the tail.
  • Flipping before saving next: the rest of the list becomes unreachable.
  • Recursive version without head.next = None: the last two nodes point at each other — a cycle.

Summary

Save next, flip curr.next to prev, advance both — once per node. The same three-pointer move appears inside harder problems: Palindrome Linked List reverses the second half, and Reorder List reverses it before weaving the halves together.

The approach, step by step

  1. Start the pointers

    Set prev to NULL and curr to head.

  2. Save, flip, advance

    While curr is not NULL: save next = curr.next, point curr.next at prev, then move prev to curr and curr to next.

  3. Return the new head

    When curr is NULL, prev is the old tail and the new head. Return it.

Frequently asked questions

Why do you need three pointers to reverse a linked list?

curr is the node being flipped, prev is where its link must point, and next holds the rest of the list. Flipping curr.next overwrites the only link to the remaining nodes, so next must be saved first or they are lost.


What is the time and space complexity of reversing a linked list?

The iterative reversal visits each node once and keeps three pointers: O(n) time and O(1) extra space. The recursive version is also O(n) time but uses O(n) call-stack space.


Should I use the iterative or the recursive version?

Both are O(n) time. The iterative one uses O(1) extra space; the recursive one uses one stack frame per node, so a very long list can overflow the call stack. Know both: the recursive one is a common follow-up.