Fanpilot

ProblemsLinked lists

Reverse Linked List

EasyLeetCode 2065 min read

You'll reverse a linked list in place by flipping one next link at a time. You need linked list nodes and assignment. Related chapter: linked lists.

1. The problem

You get the head of a linked list. Reverse it by changing its next links, and return the new head. Reuse the original nodes; don't just return the values in reverse. An empty list returns None. This is LeetCode 206.

Input Answer Why
1 → 2 → 3 3 → 2 → 1 Every next link is reversed
7 7 Only one node
Empty None No head exists

2. A correct baseline

class ListNode:
    def __init__(self,val=0,next=None):
        self.val,self.next=val,next

def build(values):
    head=None
    for value in reversed(values):
        head=ListNode(value,head)
    return head

def nodes(head):
    result=[]
    while head:
        result.append(head)
        head=head.next
    return result

def values(head):
    return [node.val for node in nodes(head)]

def baseline(head):
    original=nodes(head)
    for i,node in enumerate(original):
        node.next=original[i-1] if i else None
    return original[-1] if original else None

assert values(baseline(build([1,2,3])))==[3,2,1]
assert values(baseline(build([7])))==[7]
assert baseline(None) is None

This takes O(n) time, but also O(n) extra space to keep a list of every node. On a million nodes, that's a million extra references.

The bottleneck. It stores every node, but at any moment you only need two things: the part already reversed, and the next node to read.

3. The observation

Picture 1 → 2 → 3 halfway through: 2 → 1 → None is reversed, and 3 is still waiting. To move 3 over, point it at 2. Then the reversed part is 3 → 2 → 1 → None.

So keep two pointers: previous, the head of the reversed part, and current, the next node to move. For each node, point it back at previous. But save current.next first. Once you overwrite it, you've lost the only link to the rest of the list.

4. The solution and a trace

class ListNode:
    def __init__(self,val=0,next=None):
        self.val,self.next=val,next

class Solution:
    def reverseList(self, head):
        """Reverse an acyclic singly linked list, reusing every node."""
        previous=None
        current=head
        while current:
            following=current.next  # Save the unread suffix before rewiring.
            current.next=previous
            previous=current
            current=following
        return previous

solve=Solution().reverseList
assert values(solve(build([1,2,3])))==[3,2,1]
assert values(solve(build([7])))==[7]
assert solve(None) is None

Trace 1 → 2 → 3:

Move Reversed part Still to read
Start Empty 1 → 2 → 3
Rewire 1 1 → None 2 → 3
Rewire 2 2 → 1 → None 3
Rewire 3 3 → 2 → 1 → None Empty

5. Why it works

What stays true. previous is the head of the reversed part, and current is the head of the part still to read.

Why each step keeps it true. Saving following keeps the rest of the list reachable. Pointing current at previous moves exactly one node into the reversed part.

The result is right. Each node moves once. When current is None, every node is in the reversed part, and previous is its head.

6. Cost, edge cases, and failure

Cost. One pass: O(n) time, growing in step with the list. O(1) extra space, for three pointers.

Edge cases. The nodes and their values stay the same; only the next links change. Empty and one-node lists need no special handling. A list with a cycle has no end, so it can't be reversed this way.

Where it fails. Swapping values instead of links would return the old head with new values. The test checks that the actual nodes moved:

head=build([1,2]); first=head; second=head.next
result=solve(head)
assert result is second and result.next is first and first.next is None
Going further: random checks, related problems, and a reconstruction exercise

Check it against the baseline

import random
rng=random.Random(206)
for _ in range(500):
    data=[rng.randint(-10,10) for _ in range(rng.randrange(50))]
    head=build(data); original=nodes(head)
    result=solve(head); actual=nodes(result)
    assert actual==original[::-1]
    assert [n.val for n in actual]==values(baseline(build(data)))
# The iterative solution also handles chains beyond Python's recursion depth.
assert len(nodes(solve(build(list(range(5000))))))==5000

Related problems

Problem What changes What follows
Reverse Linked List II Reverse only positions left to right The same loop on the middle part. Keep pointers to the nodes just before and after it, to reconnect the ends.
Reverse Nodes in k-Group Reverse every group of k nodes; leave a short last group alone Before each group, check that k nodes remain. Then reverse the group and reconnect it.

Reconstruct it

Draw the reversed part and the unread part before each step. Explain exactly which link you'd lose if you changed current.next before saving it.