Reverse Linked List
Re-point each node's next to the node behind it as you walk.
Approach
Keep prev = null and iterate. For each node, remember its next, flip its pointer to prev, then advance prev and current forward. When you fall off the end, prev is the new head. It's an in-place pointer reversal in one pass.
Time complexity
O(n)
Space complexity
O(1)
Common mistake
Losing the rest of the list by overwriting node.next before saving it in a temp.
Interactive walkthrough — step through it below, free and without an account
Problem
Given the head of a singly linked list, change the links so the list is ordered in reverse. Return the node that becomes the new head after the reversal. The original nodes must be reused rather than copied into a separate list.
Examples
Input: head = [1,2,3,4]
Output: [4,3,2,1]
Input: head = [7,8]
Output: [8,7]
Problem
Problem
Given the head of a singly linked list, change the links so the list is ordered in reverse. Return the node that becomes the new head after the reversal. The original nodes must be reused rather than copied into a separate list.
Problem and Goal
Learning goal: Understand what must change when reversing a singly linked list.
Core idea
A linked list is determined by its next pointers, not by the physical order of its nodes. Reversal means every node should point to the node that appeared immediately before it. The old head becomes the tail, so its next must become null; the old tail becomes the new head.
Algorithm plan
- Start with
prev = nullandcurrent = head. - While
currentis notnull, process one link. - Save the node after
currentintemp. - Point
current.nextbackward toprev. - Advance both pointers:
prev = current, thencurrent = temp. - Return
prevafter the walk ends.
Micro-example
For 1 -> 2 -> 3 -> 4, the first reversal changes 1 -> 2 into 1 -> null. The remaining unprocessed list still begins at 2, because it was saved in temp.
Pitfalls
- The answer is the final
prev, not the originalhead. - An empty list should return
null. - A one-node list is already reversed.
Recap
- Reverse links, not node values.
- The old tail becomes the new head.
- Preserve access to the unreversed suffix before changing a pointer.
Problem and Goal
Reverse the arrows
For every node, replace its forward link with a link to the node behind it. After the final node is processed, `prev` identifies the new head.
Reversal is pointer redirection, not value swapping.
The Two-Region Invariant
Learning goal: Use prev and current to describe the reversed and unreversed parts of the list.
Core idea
At every moment, the list is split into two regions. The nodes from the new head through prev are already reversed, while current begins the untouched suffix. The key operation joins the next node from the untouched suffix to the front of the reversed region.
Key insight
After flipping current.next, the old forward direction is gone. Save the old successor first so the untouched suffix remains reachable.
Data structure model
| Variable | Meaning | Safe update order |
|---|---|---|
prev | Head of the reversed prefix | Set to current |
current | First node in the unreversed suffix | Set to saved temp |
temp | Original successor of current | Capture before the flip |
while current != null:
temp = current.next
current.next = prev
prev = current
current = tempMicro-example
Before processing 3, the reversed prefix is 2 -> 1 -> null and the suffix is 3 -> 4. Redirecting 3.next to 2 produces 3 -> 2 -> 1, while temp preserves access to 4.
Pitfalls
- Do not move
currentbefore saving its successor. - Do not confuse the reversed prefix with the original prefix.
- The invariant must hold after every loop iteration.
Recap
previs the head of the reversed region.currentis the head of the remaining region.- One saved pointer makes the in-place update safe.
The Two-Region Invariant
`prev` always starts a correctly reversed chain.
After two iterations: 2 -> 1 -> null
Main Approach: One-Pass Pointer Reversal
Learning goal: Translate the invariant into a complete iterative algorithm.
Core idea The algorithm processes each node exactly once. Before changing a link, it records where the unreversed suffix continues; then it flips one arrow and advances the boundary between the two regions. Because no new nodes or auxiliary list are needed, the reversal happens in place.
Algorithm plan
- Initialize
prevtonullandcurrenttohead. - Store
current.nextintemp. - Set
current.next = prevto reverse one link. - Move
prevtocurrent. - Move
currenttotemp. - Return
prevwhencurrentreachesnull.
def reverseList(head):
prev, current = None, head
while current:
temp = current.next
current.next = prev
prev = current
current = temp
return prevMicro-example
For [1,2,3,4], the boundary moves as follows: null | 1 2 3 4, then 1 | 2 3 4, then 2 1 | 3 4, and finally 4 3 2 1 |.
Pitfalls
- The assignment
current.next = prevmust occur aftertemp = current.next. - Returning
headreturns the old first node, now the tail. - Avoid allocating an array or a second set of nodes; neither is necessary.
Recap
- Each iteration saves, flips, and advances.
- The loop ends exactly when the original suffix is exhausted.
- The final
previs the reversed list's head.
Main Approach: One-Pass Pointer Reversal
| Approach | Time | Extra space | Key tradeoff |
|---|---|---|---|
| Iterative pointer reversal | O(n) | O(1) | One pass; changes links in place |
| Recursive reversal | O(n) | O(n) | Call stack grows with list length |
| Copy values to an array | O(n) | O(n) | Uses extra storage and does not reverse links directly |
Walkthrough: Reverse 1 → 2 → 3 → 4
Learning goal: Trace every pointer update on the example [1,2,3,4].
Core idea Each iteration performs one local reversal while preserving the rest of the chain. The visualization separates the saved successor from the pointer that is being rewritten, making the critical update order explicit.
Pointer movement
| Iteration | prev before | current | Saved temp | New reversed prefix |
|---|---|---|---|---|
| 1 | null | 1 | 2 | 1 -> null |
| 2 | 1 | 2 | 3 | 2 -> 1 -> null |
| 3 | 2 | 3 | 4 | 3 -> 2 -> 1 -> null |
| 4 | 3 | 4 | null | 4 -> 3 -> 2 -> 1 -> null |
Why it works
After processing node k, every node through k points backward, and current still identifies the untouched suffix.
Micro-example
At the final iteration, current is 4 and temp is null. Setting 4.next = 3 completes the reversal; advancing makes current = null and leaves prev = 4.
Pitfalls
- The old edge
3 -> 4must be remembered before changing4.next. - A
nulltempis normal on the final iteration. - Follow
nextfrom the returned node to verify the final order.
Recap
- Four nodes require four pointer flips.
- The final chain is
4 -> 3 -> 2 -> 1 -> null. previs the only pointer needed after the loop.
Walkthrough: Reverse 1 → 2 → 3 → 4
Traces the concrete example 1 -> 2 -> 3 -> 4 from initialization to the reversed result.
Pitfalls and Edge Cases
Learning goal: Recognize the pointer-ordering mistake that can destroy access to the remaining list.
Core idea
The dangerous line is current.next = prev. It is necessary, but it overwrites the only link from current to the unreversed suffix. Therefore, temp = current.next must happen first. This is the central safety rule for in-place linked-list mutation.
Common pitfall
If you overwrite current.next before saving it, the rest of the list is lost. The loop can no longer reach the next unprocessed node.
Edge cases
- Empty list:
head = null; the loop does not run and the result isnull. - One node: Its link is already
null; return that node unchanged. - Two nodes: One flip changes
a -> bintob -> a. - Long list: The same constant-space loop works without recursion depth concerns.
Micro-example
For 1 -> 2 -> 3, the incorrect order sets 1.next = null and then tries to advance using 1.next; node 2 is no longer reachable. The correct order stores node 2 in temp before setting 1.next = null.
Pitfalls
- Do not use the changed
current.nextas the next node. - Do not return the old head.
- Do not create a cycle by pointing the final node anywhere except its processed predecessor.
Recap
- Save, flip, advance is the safe order.
nullinputs are handled naturally.- Each link must be changed once, with no accidental cycle.
Pitfalls and Edge Cases
For current = 1 in 1 -> 2 -> 3, which statement must happen first?
Complexity and Wrap-up
Learning goal: Justify why iterative reversal meets the target efficiency.
Core idea
The loop visits each node once and performs a constant amount of pointer work per visit: one temporary assignment, one link reversal, and two pointer advances. It uses only prev, current, and temp, regardless of the list length.
Complexity analysis
- Time:
O(n), because allnnodes are processed once. - Space:
O(1)auxiliary space, because the algorithm stores only a fixed number of pointers. - Output structure: The original nodes are reused; no values or nodes are copied.
Micro-example
For four nodes, there are four iterations and four link flips. For ten thousand nodes, there are still only three working references; the number of references does not grow with n.
Why it works
The invariant preserves both regions: the reversed prefix is valid, and the saved successor preserves the untouched suffix. When the suffix is empty, the prefix is the complete answer.
Pitfalls
O(1)space refers to auxiliary memory, not the input list itself.- A recursive solution has
O(n)call-stack space, so it does not meet the constant-space target. - Linear time is necessary because every link must be redirected.
Recap
- Save the successor, reverse one link, and advance.
- Return
prevaftercurrentbecomesnull. - Final target:
O(n)time andO(1)extra space.
Complexity and Wrap-up
Summarizes the cost of the one-pass in-place reversal.
Now try it on your own problem
Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.
Build a lesson from any problem
