datarekha
Coding Patterns Easy Asked at AmazonAsked at GoogleAsked at Meta

Reverse a singly linked list, iteratively and recursively.

The short answer

Iteratively: walk the list with three pointers — prev, current, and next — rewiring each node's pointer as you go. Recursively: reverse the rest of the list, then attach the current head to the new tail. Both are O(n) time, O(1) and O(n) space respectively.

How to think about it

Reversal is a pointer-discipline question in disguise. You cannot index into a linked list, so the interviewer is watching whether you carry the right state in local variables as you walk and rewire each node without dropping the rest of the chain. The core move is to flip one arrow at a time: a node that pointed forward to next should instead point back to prev. The catch is that overwriting curr.next destroys your only handle on the remaining list — so you save next into a temporary before you flip.

That gives the iterative three-pointer loop: hold prev (the reversed part behind you), curr (the node you are flipping), and a saved nxt (the node ahead). Flip curr.next = prev, then march all three forward. When curr falls off the end, prev is the new head. It uses no extra data structure — pure O(1) space. The recursive form says the same thing differently: reverse the tail first, then hang the current head onto the end of it (head.next.next = head; head.next = None). It reads elegantly but spends O(n) stack frames, so on a very long list it can overflow — prefer the iterative version in production.

A worked example

class Node:
    def __init__(self, val, nxt=None):
        self.val = val
        self.next = nxt

def to_list(head):                 # for printing only
    out = []
    while head:
        out.append(head.val)
        head = head.next
    return out

# Iterative: O(n) time, O(1) space
def reverse_iter(head):
    prev = None
    curr = head
    while curr:
        nxt = curr.next            # save BEFORE you overwrite the pointer
        curr.next = prev           # flip the arrow
        prev = curr                # advance the reversed part
        curr = nxt                 # advance into the rest
    return prev

# Recursive: O(n) time, O(n) call-stack space
def reverse_rec(head):
    if not head or not head.next:
        return head
    new_head = reverse_rec(head.next)
    head.next.next = head          # tail's next now points back at head
    head.next = None               # head becomes the new tail
    return new_head

head = Node(1, Node(2, Node(3, Node(4, Node(5)))))
print(to_list(reverse_iter(head)))   # five nodes, iterative

head2 = Node(1, Node(2, Node(3)))
print(to_list(reverse_rec(head2)))   # three nodes, recursive

head3 = Node(42)
print(to_list(reverse_iter(head3)))  # single node -> unchanged

print(to_list(reverse_iter(None)))   # empty list -> empty
[5, 4, 3, 2, 1]
[3, 2, 1]
[42]
[]

The first two lines show both methods landing on the same answer — the order is fully reversed. The single-node case returns [42] untouched, and the empty list returns []: both base cases are handled because reverse_iter simply never enters its loop, and the printed lists confirm no node was lost in the rewiring.

Keep practising

All Coding Patterns questions

Explore further

Skip to content