Subtitle / Summary
Reverse Linked List is the first serious pointer-rewiring exercise in Hot100. It looks simple, but most bugs come from broken links and wrong operation order. This ACERS guide explains the three-pointer iterative template thoroughly and compares it with recursion.
- Reading time: 10-12 min
- Tags:
Hot100,linked list,pointer,iteration - SEO keywords: Hot100, Reverse Linked List, three pointers, iterative, recursive, LeetCode 206
- Meta description: Three-pointer iterative reversal in O(n)/O(1), with recursive contrast, common pitfalls, engineering mapping, and runnable multi-language implementations.
A - Algorithm (Problem and Algorithm)
Problem Restatement
Given the head of a singly linked list, reverse the list and return the new head.
Input / Output
| Name | Type | Description |
|---|---|---|
| head | ListNode | head of singly linked list (can be null) |
| return | ListNode | new head after reversal |
Example 1
input: 1 -> 2 -> 3 -> 4 -> 5 -> null
output: 5 -> 4 -> 3 -> 2 -> 1 -> null
Example 2
input: 1 -> 2 -> null
output: 2 -> 1 -> null
Target Readers
- Hot100 learners and interview candidates
- Developers who often hit null-pointer or broken-chain bugs in list problems
- Engineers who want stable pointer manipulation patterns in C/C++/Rust/Go
Background / Motivation
In production code, “reverse linked list” may not appear as a LeetCode function, but the skill is highly transferable:
- Reorder nodes in-place with O(1) extra memory
- Keep link integrity while changing direction
- Handle
head = nulland single-node lists without special-case chaos
If this template is truly internalized, many list problems become straightforward: reverse sublist, reverse k-group, palindrome list, and so on.
Core Concepts
- Singly linked list: each node has one
nextpointer - Broken-link risk: if you overwrite
cur.nextbefore saving old next, you lose the remaining chain - Three pointers (
prev,cur,next): save successor, reverse link, then advance - Loop invariant:
previs always the head of the already reversed partcuris always the head of the not-yet-processed part
C - Concepts (Core Ideas)
How To Build The Solution From Scratch
Step 1: Start from the smallest useful example
Take:
1 -> 2 -> 3 -> null
The target is not to build a new list with reversed values. It is to turn the links into:
3 -> 2 -> 1 -> null
So this problem is really about pointer direction, not value order.
Step 2: Decide what the partial answer must remember
At any point we need to know two list fronts:
- the head of the already reversed prefix
- the head of the not-yet-processed suffix
That is exactly:
prev = None
cur = head
Here:
prevmeans “head of the reversed part”curmeans “head of the remaining part”
Step 3: Ask why one more variable is necessary
Before reversing this edge:
cur.next = prev
we must save the original successor, or the rest of the list becomes unreachable.
So every round begins with:
nxt = cur.next
This is the most important safety move in the whole problem.
Step 4: Define one loop iteration precisely
Each iteration does only one job:
move
curfrom the untouched suffix to the front of the reversed prefix.
That forces the order:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
If you reorder these lines carelessly, you usually break the chain.
Step 5: Define when the work is complete
When cur is None, the untouched suffix is empty.
So every node is already inside the reversed prefix, and the new head is:
return prev
Step 6: Walk one branch slowly
Still using:
1 -> 2 -> 3 -> null
Start:
prev = nullcur = 1
After one round:
1.next = nullprev = 1cur = 2
After two rounds:
2.next = 1prev = 2cur = 3
After three rounds:
3.next = 2prev = 3cur = null
So the answer is 3.
Step 7: Reduce the whole template to one invariant
The reusable statement is:
prevalways points to the reversed prefix, andcuralways points to the untouched suffix.
Once that invariant feels stable, 206, 92, 25, and 234 all become easier.
Assemble the Full Code
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_list(head):
prev = None
cur = head
while cur is not None:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
def from_list(arr):
dummy = ListNode()
tail = dummy
for x in arr:
tail.next = ListNode(x)
tail = tail.next
return dummy.next
def to_list(head):
ans = []
while head:
ans.append(head.val)
head = head.next
return ans
if __name__ == "__main__":
h = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_list(h)))
Reference Answer
from typing import Optional
class ListNode:
def __init__(self, val: int = 0, next: Optional["ListNode"] = None):
self.val = val
self.next = next
def reverseList(head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
cur = head
while cur is not None:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
Method Category
- In-place linked-list manipulation
- Iterative simulation
- Recursion as equivalent reference solution
Loop Invariant (why it is correct)
At each loop start:
prevpoints to a valid reversed listcurpoints to the first node not yet reversed- Original nodes are partitioned into:
- reversed prefix
- untouched suffix
Each iteration moves exactly one node from untouched suffix to reversed prefix.
When cur == null, all nodes are in reversed prefix, and prev is the new head.
Recursive counterpart
Recursive idea:
- Reverse
head.nextonward and getnew_head - Let
head.next.next = head - Set
head.next = nullto cut old forward link
Readable, but stack usage is O(n).
Practical Guide / Steps
- Initialize
prev = null,cur = head - While
cur != null:next = cur.nextcur.next = prevprev = curcur = next
- Return
prev
Runnable Python example (reverse_list.py):
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_list(head):
prev = None
cur = head
while cur is not None:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
def from_list(arr):
dummy = ListNode()
tail = dummy
for x in arr:
tail.next = ListNode(x)
tail = tail.next
return dummy.next
def to_list(head):
ans = []
while head:
ans.append(head.val)
head = head.next
return ans
if __name__ == "__main__":
h = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_list(h)))
Explanation / Why This Works
The order of operations is the whole point:
- Save
nextfirst (avoid losing remaining chain) - Reverse current edge (
cur.next = prev) - Advance both pointers
If you swap step 1 and 2, the rest of list may become unreachable. That is the most common bug in this problem.
E - Engineering (Real-world Scenarios)
Scenario 1: free-list reorder in memory-oriented systems (C)
Background: some allocators keep free blocks in a singly linked free-list.
Why it fits: reversing list order is an in-place strategy to alter reuse order without allocating memory.
#include <stdio.h>
typedef struct Node {
int id;
struct Node* next;
} Node;
Node* reverse(Node* head) {
Node* prev = NULL;
Node* cur = head;
while (cur) {
Node* nxt = cur->next;
cur->next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
int main(void) {
Node c = {3, NULL};
Node b = {2, &c};
Node a = {1, &b};
Node* head = reverse(&a);
for (Node* p = head; p; p = p->next) printf("%d ", p->id);
printf("\n");
return 0;
}
Scenario 2: server-side operation stack replay direction switch (Go)
Background: a lightweight task chain is stored as a singly linked stack.
Why it fits: reversing in-place switches replay direction without extra containers.
package main
import "fmt"
type Node struct {
Val int
Next *Node
}
func reverse(head *Node) *Node {
var prev *Node
cur := head
for cur != nil {
nxt := cur.Next
cur.Next = prev
prev = cur
cur = nxt
}
return prev
}
func main() {
head := &Node{1, &Node{2, &Node{3, nil}}}
head = reverse(head)
for p := head; p != nil; p = p.Next {
fmt.Print(p.Val, " ")
}
fmt.Println()
}
Scenario 3: pointer-animation teaching in browser (JavaScript)
Background: in algorithm visualization, object references simulate list nodes.
Why it fits: the three-pointer state transition is easy to animate frame-by-frame.
function Node(val, next = null) {
this.val = val;
this.next = next;
}
function reverse(head) {
let prev = null;
let cur = head;
while (cur) {
const nxt = cur.next;
cur.next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
let head = new Node(1, new Node(2, new Node(3)));
head = reverse(head);
const out = [];
for (let p = head; p; p = p.next) out.push(p.val);
console.log(out.join(" "));
R - Reflection
Complexity
- Iterative:
- Time:
O(n) - Space:
O(1)
- Time:
- Recursive:
- Time:
O(n) - Space:
O(n)(call stack)
- Time:
Alternatives and Tradeoffs
| Method | Time | Extra Space | Tradeoff |
|---|---|---|---|
| Rebuild new list | O(n) | O(n) | Easy but not in-place |
| Recursive reversal | O(n) | O(n) | Elegant, but stack-risk on long lists |
| Three-pointer iterative | O(n) | O(1) | Best engineering baseline |
Common Mistakes
- Rewire before saving
next(break chain) - Forget advancing
cur(infinite loop) - Recursive version forgetting
head.next = null(cycle risk) - Reversing values instead of links (misses structural requirement)
Why iterative is most practical
- Stack-safe for very long lists
- Local, inspectable pointer transitions
- Better fit for systems programming and production safety constraints
FAQ and Notes
What about empty list or single node?
The same loop handles both naturally.Is three pointers mandatory?
You must preserve successor somehow; variable names can differ, state is equivalent.Why not prefer recursion since it’s shorter?
Recursive depth can overflow stack on long input; iterative is safer as default.How to self-check quickly?
Use this rule: save next first, reverse next, then advance.
Best Practices
- Memorize the operation order as a fixed template
- Draw pointer state for at least 2-3 iterations before coding
- Use iterative as default in production-quality code
S - Summary
- Reverse Linked List is fundamentally pointer rewiring, not value swapping.
- Three-pointer iterative template achieves
O(n)time andO(1)extra space. - Correct order is the core: save successor -> reverse link -> advance.
- Recursive form is useful for understanding, but iterative is usually safer for engineering.
Recommended Further Reading
- LeetCode 206. Reverse Linked List
- LeetCode 92. Reverse Linked List II
- LeetCode 25. Reverse Nodes in k-Group
- LeetCode 234. Palindrome Linked List
Conclusion
Once the three-pointer template is stable in your muscle memory, most linked-list reversal variants become local modifications rather than new problems.
References
- https://leetcode.com/problems/reverse-linked-list/
- https://en.cppreference.com/w/cpp/container/forward_list
- https://doc.rust-lang.org/std/option/enum.Option.html
- https://go.dev/tour/moretypes/6
Meta Info
- Reading time: 10-12 min
- Tags: Hot100, linked list, pointer, iteration, LeetCode 206
- SEO keywords: Reverse Linked List, three pointers, iterative, recursive, LeetCode 206, Hot100
- Meta description: O(n)/O(1) linked-list reversal with three pointers, with recursive comparison and runnable multi-language implementations.
Call To Action (CTA)
Do two drills to lock this in:
- Manually trace
prev/cur/nextfor at least three steps without code - Solve LeetCode 92 right after this one to reuse the same rewiring pattern
Multi-language Implementations (Python / C / C++ / Go / Rust / JS)
from typing import Optional
class ListNode:
def __init__(self, val: int = 0, next: Optional["ListNode"] = None):
self.val = val
self.next = next
def reverseList(head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
cur = head
while cur is not None:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int val;
struct ListNode* next;
};
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* prev = NULL;
struct ListNode* cur = head;
while (cur) {
struct ListNode* nxt = cur->next;
cur->next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
#include <iostream>
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* cur = head;
while (cur) {
ListNode* nxt = cur->next;
cur->next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
package main
type ListNode struct {
Val int
Next *ListNode
}
func reverseList(head *ListNode) *ListNode {
var prev *ListNode
cur := head
for cur != nil {
nxt := cur.Next
cur.Next = prev
prev = cur
cur = nxt
}
return prev
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
pub val: i32,
pub next: Option<Box<ListNode>>,
}
impl ListNode {
pub fn new(val: i32) -> Self {
ListNode { val, next: None }
}
}
pub fn reverse_list(mut head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
let mut prev: Option<Box<ListNode>> = None;
while let Some(mut node) = head {
head = node.next.take();
node.next = prev;
prev = Some(node);
}
prev
}
function ListNode(val, next = null) {
this.val = val;
this.next = next;
}
function reverseList(head) {
let prev = null;
let cur = head;
while (cur) {
const nxt = cur.next;
cur.next = prev;
prev = cur;
cur = nxt;
}
return prev;
}