Reverse Linked List is rated Easy β but it is the single most important linked list problem you will ever solve. Every harder linked list problem (Reverse in k-Group, Palindrome Linked List, Reorder List) uses this as a building block. If you don't understand the mechanics deeply, those harder problems become nearly impossible.
Our analysis of 10,385 verified interview questions across 259 companies shows it appeared 27 times across 13 companies β including Google, Meta, Apple, Microsoft, Bloomberg, ByteDance, and J.P. Morgan.
This guide goes beyond just showing you the code. We'll break down exactly why each pointer move happens in the iterative solution, how the call stack unwinds in the recursive solution, what the most common mistakes are and why they happen, and how this problem directly unlocks 6 harder variants.
| Metric | Value |
|---|---|
| Total Interview Appearances | 27 verified mentions |
| Companies That Ask It | 13 companies |
| Difficulty | Easy |
| Core Patterns | Linked List, Recursion |
Google, Meta, Apple, Microsoft, Bloomberg, ByteDance, J.P. Morgan, Cisco, Expedia, Accenture, Deloitte, Luxoft, and NetApp.
LeetCode #206 β Reverse Linked List
Given the head of a singly linked list, reverse the list and return the reversed list's head.
Input: head = [1 β 2 β 3 β 4 β 5] β Output: [5 β 4 β 3 β 2 β 1]
Input: head = [1 β 2] β Output: [2 β 1]
Input: head = [] β Output: []
A singly linked list node only has a val and a next pointer. There is no prev pointer. The list only knows how to go forward. Your job is to make every node point backward instead β in-place, without creating a new list.
You cannot simply "walk backward" through a singly linked list. Once you redirect curr.next, you permanently lose access to the rest of the list unless you saved it first. This is the entire challenge of the problem.
Most beginners try something like this on their first attempt:
# What beginners try:
curr = head
while curr:
curr.next = prev # β redirects the pointer
prev = curr
curr = curr.next # β curr.next is now prev (already changed!)
# we've lost the rest of the list forever
The problem is the order of operations. The moment you write curr.next = prev, the original curr.next (the next node in the list) is gone. You can never reach it again. So on the next line, curr = curr.next moves curr to prev, not forward β creating an infinite loop or immediately terminating.
The fix is simple but crucial: save curr.next before you redirect anything. This is the entire secret of the iterative solution.
The iterative solution uses three pointers that each have a specific role:
prev β represents the already-reversed portion of the list. Starts at None because the new tail (originally the head) should point to null.curr β the node currently being processed. Starts at head.next_node β a temporary variable that saves curr.next before we break the forward link. Without this, we'd lose the rest of the list.At each iteration, you execute exactly four operations in this exact order:
next_node = curr.next β preserve the forward reference before breaking itcurr.next = prev β point current node backwardprev = curr β the reversed portion now includes currcurr = next_node β move to the next unprocessed nodeWhen curr becomes None, the entire list has been reversed and prev is pointing at the new head (the original last node). Return prev.
Let's trace through [1 β 2 β 3 β 4 β 5] completely:
Initial state:
prev = None
curr = 1
βββββββββββββββββββββββββββββββββββββ
Iteration 1: curr = Node(1)
next_node = 2 β save: next_node points to Node(2)
curr.next = None β redirect: 1 now points to None (prev)
prev = Node(1) β advance prev: reversed portion is [1 β None]
curr = Node(2) β advance curr: move forward
State: None β 1 2 β 3 β 4 β 5
βprev βcurr
βββββββββββββββββββββββββββββββββββββ
Iteration 2: curr = Node(2)
next_node = 3 β save Node(3)
curr.next = Node(1) β redirect: 2 now points to 1
prev = Node(2) β advance prev: reversed portion is [2 β 1 β None]
curr = Node(3) β advance curr
State: None β 1 β 2 3 β 4 β 5
βprev βcurr
βββββββββββββββββββββββββββββββββββββ
Iteration 3: curr = Node(3)
next_node = 4
curr.next = Node(2) β 3 now points to 2
prev = Node(3)
curr = Node(4)
State: None β 1 β 2 β 3 4 β 5
βprev βcurr
βββββββββββββββββββββββββββββββββββββ
Iteration 4: curr = Node(4)
next_node = 5
curr.next = Node(3) β 4 now points to 3
prev = Node(4)
curr = Node(5)
State: None β 1 β 2 β 3 β 4 5
βprev βcurr
βββββββββββββββββββββββββββββββββββββ
Iteration 5: curr = Node(5)
next_node = None β save None (end of list)
curr.next = Node(4) β 5 now points to 4
prev = Node(5)
curr = None β advance curr: now None
State: None β 1 β 2 β 3 β 4 β 5
βprev curr=None
βββββββββββββββββββββββββββββββββββββ
curr is None β loop ends β return prev = Node(5)
Result: 5 β 4 β 3 β 2 β 1 β None β
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverseList(head: ListNode) -> ListNode:
prev = None
curr = head
while curr:
next_node = curr.next # Step 1: save β MUST come first
curr.next = prev # Step 2: redirect β break forward, point back
prev = curr # Step 3: advance prev β grow the reversed portion
curr = next_node # Step 4: advance curr β move to next unprocessed node
# When curr is None, prev points to the new head
return prev
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextNode = curr.next; // Step 1: save
curr.next = prev; // Step 2: redirect
prev = curr; // Step 3: advance prev
curr = nextNode; // Step 4: advance curr
}
return prev; // new head
}
}
var reverseList = function(head) {
let prev = null;
let curr = head;
while (curr) {
const nextNode = curr.next; // Step 1: save
curr.next = prev; // Step 2: redirect
prev = curr; // Step 3: advance prev
curr = nextNode; // Step 4: advance curr
}
return prev; // new head
};
The recursive approach is more elegant but harder to understand because the key action happens on the way back up the call stack, not on the way down.
Here is the mental model in two phases:
We keep calling reverseList(head.next) recursively, doing nothing except moving forward, until we hit the base case: a node with no next (the last node). That last node becomes our new_head β the head of the final reversed list. We return it all the way up unchanged.
Now, on the way back up, at each stack frame we have access to head (the current node) and we know head.next still points to the next node (which is already part of the reversed suffix). We do two things:
head.next.next = head β the node after head should now point back to head. This wires head into the reversed chain.head.next = None β break the original forward link from head to its successor. If we don't do this, head still points forward AND backward, creating a cycle.Then we return new_head unchanged all the way up. Every stack frame does the same two operations β wire the current node into the reversed chain, break the forward link β and passes the same new_head up.
ββββββββββββββββ DESCENDING (building call stack) ββββββββββββββββ
reverseList(1) β calls reverseList(2)
reverseList(2) β calls reverseList(3)
reverseList(3) β head.next is None β BASE CASE
return Node(3) as new_head
ββββββββββββββββ ASCENDING (unwinding call stack) ββββββββββββββββ
Back in reverseList(2): head = Node(2), new_head = Node(3)
At this point: 1 β 2 β 3 β None (original forward links still exist)
head.next.next = head
β Node(3).next = Node(2) β 3 now points back to 2
β State: 1 β 2 β 3 (2 still points forward to 3 too β cycle risk!)
head.next = None
β Node(2).next = None β break 2's forward link
β State: 1 β 2 3 β 2 β None β wait, let's re-read...
β Actually: 1 β 2 β None and 3 β 2 β None
(2's forward link is broken, 3 now owns 2)
return new_head (Node 3) β pass it up unchanged
ββββββββββββββββ
Back in reverseList(1): head = Node(1), new_head = Node(3)
At this point: 1 β 2 β None (2 was already severed from 3)
3 β 2 β None (reversed suffix is built)
head.next.next = head
β Node(2).next = Node(1) β 2 now points back to 1
β State: 3 β 2 β 1 (1 still forward-links... to null already)
head.next = None
β Node(1).next = None β confirm 1 points to null (already was)
return new_head (Node 3)
ββββββββββββββββ
Final result: 3 β 2 β 1 β None β
new_head = Node(3) returned to original caller
head.next = None is CriticalAfter head.next.next = head, both head and head.next point to each other β a cycle. The line head.next = None breaks head's forward link, converting the cycle into a clean chain. Forgetting this line creates an infinite loop when traversing the result.
def reverseList(head: ListNode) -> ListNode:
# Base case: empty list or single node β already reversed
if not head or not head.next:
return head
# Phase 1 (descend): recurse all the way to the last node
# new_head will be the last node, returned unchanged all the way up
new_head = reverseList(head.next)
# Phase 2 (ascend): wire current node into the reversed chain
# head.next is the node immediately after head β it's already in the reversed suffix
head.next.next = head # make that node point BACK to head
head.next = None # break head's original forward link (prevents cycle)
# Pass new_head (the last node) unchanged up the call stack
return new_head
class Solution {
public ListNode reverseList(ListNode head) {
// Base case
if (head == null || head.next == null) return head;
// Descend to the end, capture new head (last node)
ListNode newHead = reverseList(head.next);
// Ascend: wire current node into reversed chain
head.next.next = head; // node after head points back to head
head.next = null; // break forward link, prevent cycle
return newHead; // propagate unchanged
}
}
var reverseList = function(head) {
// Base case
if (!head || !head.next) return head;
// Descend to end, capture new head
const newHead = reverseList(head.next);
// Ascend: wire current node into reversed chain
head.next.next = head; // node after head points back to head
head.next = null; // break forward link, prevent cycle
return newHead; // propagate unchanged
};
Writing curr.next = prev before saving next_node = curr.next. Once you redirect curr.next, the original next node is permanently lost. The save step must always come first.
curr.next = prev; next_node = curr.next; ...next_node = curr.next; curr.next = prev; ...
head.next = None (Recursive)After head.next.next = head, nodes point to each other creating a cycle: head β next β head β next β .... Without head.next = None, printing or traversing the result causes an infinite loop. The original tail (node 1 in a 5-node list) already has next = None from its base case return, but every other node needs it explicitly set.
head Instead of prev (Iterative)After the while loop, head still points to the original first node (now the tail). prev is the original last node (now the head). Always return prev.
If head is None (empty list), both solutions handle it correctly β the iterative loop doesn't execute and returns prev = None; the recursive base case returns None. But if you write a custom base case as if not head.next only, you'll get an AttributeError on None.next. Always check if not head or not head.next.
| Approach | Time | Space | Notes |
|---|---|---|---|
| Iterative | O(n) | O(1) | Visits each node once. Only 3 pointer variables regardless of list size. |
| Recursive | O(n) | O(n) | Each call adds a frame to the call stack. For n=5000 nodes, that's 5000 stack frames β risk of stack overflow on very large inputs. |
In interviews, always lead with the iterative solution. The recursive solution is elegant and worth knowing, but the O(n) space cost from the call stack is a real drawback that interviewers will ask about.
Once you own LC #206, these six problems become significantly more approachable β they all use reversal as a core subroutine:
| Problem | How It Builds on #206 | Difficulty |
|---|---|---|
| LC #92 β Reverse Linked List II | Reverse only nodes from position left to right. Requires finding the sublist boundaries first, then applying the same three-pointer reversal on just that segment. | Medium |
| LC #25 β Reverse Nodes in k-Group | Reverse every k consecutive nodes. Checks if k nodes remain, reverses them, reconnects groups. Uses reversal as a helper function called repeatedly. | Hard |
| LC #234 β Palindrome Linked List | Find the middle (Floyd's algorithm), reverse the second half, compare both halves node by node, then optionally restore. Reversal is one of three steps. | Easy |
| LC #143 β Reorder List | Find middle, reverse second half, interleave first and second halves. Identical reversal subproblem as #234. | Medium |
| LC #24 β Swap Nodes in Pairs | Special case of k-group reversal with k=2. Can be solved iteratively by manipulating 4 pointers per pair. | Medium |
| LC #61 β Rotate List | Find the new tail (length - k % length steps in), reverse the connection. Uses reversal concept but solves it via pointer reconnection instead. | Medium |
Do them in this sequence: #206 β #92 β #234 β #143 β #24 β #25. Each problem adds exactly one new concept on top of the previous. By the time you reach #25 (Hard), you'll find it manageable because you've built the foundation incrementally.
Remember the four operations in order: Save β Redirect β Advance prev β Advance curr. If you forget the order in an interview, say "SRAA" mentally and you'll reconstruct the solution correctly every time.
Phase 1 is passive β just descend to the last node. Phase 2 is active β head.next.next = head wires the node in, head.next = None breaks the cycle. Two lines. Same two lines at every stack frame.
[1β2β3] on paper for the recursive version β draw the call stack explicitly.left, apply the three-pointer reversal for right - left steps, then reconnect the four boundary pointers.Tag on DSAPrep.dev under Linked List and Recursion.