Reverse Linked List in Python
Learn how to reverse a linked list in Python! An easy, fun, beginner-friendly guide to solving this classic coding interview algorithm. Includes O(1) space iterative code.
Try it yourself
Run this code directly in your browser. Click "Open in full editor" to experiment further.
Click Run to see output
Or press Ctrl + Enter
How it works
Reverse linked list python — if one algorithm question has launched a thousand whiteboard interviews, this is it. Leetcode 206: take 1 → 2 → 3 → 4 → 5 and return 5 → 4 → 3 → 2 → 1. Five lines, zero hand-waving. It's the canonical pointer manipulation python drill — a tiny problem that ruthlessly exposes whether you understand references, mutation, and order of operations. 🧠
If you can explain linked list reversal out loud while writing it, you can handle the rest of the trees-and-graphs gauntlet.
What's a Linked List?
A linked list is a chain of nodes, where each node holds a value and a next pointer to the node after it. The last node's next is None.
next and prev. Two-way. ↔️A Python list ([1, 2, 3]) is not a linked list. It's a dynamic array — contiguous memory with O(1) random access. A real linked list has O(n) access but O(1) insertion if you already hold a pointer. Different beasts.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = nextThe Iterative Approach
The iterative solution uses three pointers: prev, curr, and a temporary next_temp. Three steps repeat until curr walks off the end:
1. Save the next node so we don't lose the rest of the list.
2. Rewire curr.next to point backwards at prev.
3. Advance prev and curr one step forward.
Reverse 1 → 2 → 3 → 4. Initial: prev = None, curr = 1.
| Iteration | prev | curr | next_temp | After rewire |
|---|---|---|---|---|
| Start | None | 1 | — | 1 → 2 → 3 → 4 |
| 1 | 1 | 2 | 2 | None ← 1, 2 → 3 → 4 |
| 2 | 2 | 3 | 3 | None ← 1 ← 2, 3 → 4 |
| 3 | 3 | 4 | 4 | None ← 1 ← 2 ← 3, 4 |
| 4 | 4 | None | None | None ← 1 ← 2 ← 3 ← 4 |
When curr is None, prev sits on the new head. Return prev. That's it.
The Recursive Approach
The recursive version is gorgeous but bends your brain the first time. Three beats:
1. Base case: if the list is empty or one node, it's already reversed. Return it.
2. Recurse first: hand head.next to the function; trust it returns a reversed sublist whose tail is your immediate neighbour.
3. Rewire on the way back up: your neighbour (head.next) points at None. Tell it to point at you (head.next.next = head), then break your own forward link (head.next = None) so you don't form a cycle.
def reverse(head):
if not head or not head.next:
return head
new_head = reverse(head.next)
head.next.next = head
head.next = None
return new_headThe head.next.next = head line is the one people stare at. Read it as: "the node in front of me — point its next back at me." Once that clicks, the rest collapses into common sense.
Complexity Analysis
| Approach | Time | Space | Notes |
|---|---|---|---|
| Iterative | O(n) | O(1) | Three pointers, one pass. Interview-preferred. |
| Recursive | O(n) | O(n) | Elegant, but the call stack costs n frames. Risk of RecursionError. |
Both touch every node once. The space gap is the whole story — and it's why senior engineers reach for the iterative version first.
Common Pitfalls
curr.next = prev before saving next_temp = curr.next makes the rest of the list vanish. Save first, then mutate. ⚠️curr is None. The new head is prev. Return the wrong one and you hand back an empty list.if not head or not head.next. Either alone misses a case.None becomes the new tail's next, terminating the reversed list. Initialising prev = head is a classic bug.head now points at the tail. Deep-copy first if you need the original intact.Why Interviewers Love This
Five lines, infinite signal. It tests your pointer mental model, forces edge-case thinking (empty list, single node), makes you choose iterative vs recursive, and rewards candidates who mention O(1) space. Nowhere to hide. 🎯
Variants You Will See
left and right.k nodes; the hard-mode cousin.Real-World Uses
Why Python Devs Rarely Implement This in Real Code
In production Python you almost never write a linked list. The built-in list is a dynamic array with O(1) append and random access — perfect for 95% of cases. For queue/stack workloads, collections.deque gives O(1) at both ends. Linked lists shine only when you need $O(1)$ insertion at an arbitrary position you already hold a pointer to (intrusive lists in kernels, some graph algorithms) or low-level systems work in C/Rust. The algorithm still matters for interviews and for understanding why deque is fast. 🐍
Frequently Asked Questions
Why does Python not have a built-in linked list? Because list and collections.deque cover almost every practical case with better cache locality.
Iterative or recursive — which is better? Iterative. It's O(1) space, won't blow the stack on long lists, and is easier to reason about under pressure.
Can I reverse without modifying the original? Yes — walk the list, copy each node into a new chain, reverse as you build. Costs O(n) extra space but leaves the input untouched.
Singly vs doubly linked list reversal? For a doubly linked list you swap each node's next and prev pointers in one pass — still O(n) time, O(1) space, but symmetric.
How do I reverse only part of a list? That's LeetCode 92. Walk to left-1, reverse the segment with the same three-pointer dance, then reattach both ends.
Why is recursive $O(n)$ space? Each call adds a stack frame. For a list of length n, n frames stay pending until the base case fires — so the call stack grows linearly, even though no new nodes are allocated.