Skip to content

Coding patternsBlind 75

Reverse Linked List

Walk the list once and turn each arrow around, holding on to the next node before you cut it. Solved two ways, copying the values in O(n) time and O(n) space, and in place in O(n) time and O(1) space, with a step-through, code in five languages and the Big O worked out from the code.

You are given the head of a singly linked list. A singly linked list is a chain of nodes. Each node holds a value and a next pointer to the node after it. The last node points to null.

Reverse the list and return the new head.

Example 1

Input: head = [3, 8, 1, 6, 4]

Output: [4, 6, 1, 8, 3]

Every arrow now points the other way. The old tail, 4, is the new head.

∅38164head∅

Example 2

Input: head = [2, 9]

Output: [9, 2]

Two nodes swap places.

Example 3

Input: head = []

Output: []

An empty list stays empty.

  • 0 ≤ n ≤ 5,000, where n is the number of nodes
  • −5,000 ≤ Node.val ≤ 5,000
Can the list be empty?
Yes. Then head is null, and we return null.
May we change the nodes, or must we build a new list?
Changing the nodes is fine. We return the head of the reversed list.
Singly or doubly linked?
Singly. Each node only knows the node after it, not the one before.
Can the list have a cycle?
No. It always ends at null.

A singly linked list can only be walked forward. To reverse it, each node must end up pointing at the node before it. So while walking, keep a pointer to the previous node, and turn one arrow at each step. This is the core linked list move, and many harder problems reuse it.

Recursion also reverses the list in O(n) time, but its call stack holds n calls, so it uses O(n) memory like the copy.

In place O(n), O(1) space

Walk the list with two pointers: previous (the node behind) and current (the node we are on). At each node, turn its arrow back to previous. Then step both pointers forward.

There is one trap. Once current.next points back, the rest of the list is unreachable. So save current.next in next_node before changing it.

When current falls off the end, previous is the old tail, which is the new head.

The invariant: before each step, the nodes up to previous form a reversed list, and current is the head of the part not yet touched.

∅previous3current8164∅
  1. previous, current = None, head
  2. while current:
  3. next_node = current.next
  4. current.next = previous
  5. previous = current
  6. current = next_node
  7. return previous

previous starts at null and current at the head. Everything left of current will be reversed. Everything from current on is untouched.

1 / 12
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// In place: walk once and turn each arrow back to the node before it.
// One pass and three pointers, so O(n) time and O(1) memory.
public ListNode reverseList(ListNode head) {
ListNode previous = null, current = head;
while (current != null) {
// Save the rest of the list before we cut the link to it.
ListNode nextNode = current.next;
current.next = previous;
previous = current;
current = nextNode;
}
// current fell off the end, so previous is the old tail: the new head.
return previous;
}
}
Time O(n)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    Each run of the loop does four pointer moves. That is the same small job every time, so each run is 1 step. There is one loop.

    1. previous, current = None, head
    2. while current:
    3. next_node = current.next
    4. current.next = previous
    5. previous = current
    6. current = next_node
    7. return previous
  2. 2 Count the runs on Example 1

    Example 1 has 5 nodes. current visits each of them once, then becomes null and the loop stops.

    5 runs.

    run 1run 2run 3run 4run 5stop
    current38164null
  3. 3 Write the count for any n

    The loop runs once per node:

    n times

    Check it with Example 1: 5 nodes, 5 runs.

  4. 4 Keep only the part that grows fastest

    There is nothing to drop. The count is already n.

    So the time is O(n).

  5. S Space: count the extra memory the same way

    The code keeps three pointers: previous, current and next_node. That is 3 variables, for any n, so the space is O(1). No new nodes are made.

    previouscurrentnext_node

2,000 random lists of 0 to 8 nodes, so the empty list and the single node come up often. Copying the values and reversing in place gave the same list every time.

Largest inputsResult
5,000 nodes (the most allowed). In place.Under 0.001 s, and under 0.001 MB of extra memory: just three pointers.
5,000 nodes. Copy the values.Under 0.001 s, and about 0.04 MB of extra memory: an array of 5,000 values, which is the O(n) space.