12 KiB
Reverse Linked List — Memory Walkthrough
This walkthrough explains the classic three-pointer linked list reversal using a memory-oriented view.
The goal is not only to show that the algorithm works, but also to show what happens to:
- stack variables
- heap nodes
nextfields inside each node
The example list contains three nodes:
1 -> 2 -> 3 -> null
For clarity, fake addresses are used:
Node 1: 0x1000
Node 2: 0x2000
Node 3: 0x3000
These addresses are illustrative only.
A real program will use different addresses.
Algorithm
Node* reverseList(Node* head) {
Node* previous = nullptr;
Node* current = head;
while(current != nullptr) {
Node* next = current->next;
current->next = previous;
previous = current;
current = next;
}
return previous;
}
The three important pointers are:
| Pointer | Meaning |
|---|---|
previous |
Head of the already reversed part |
current |
Node currently being processed |
next |
Saved pointer to the remaining original list |
The most important rule is:
Save
nextbefore changingcurrent->next.
Otherwise the rest of the original list may become unreachable.
Step 0 — Initial State
Before the loop starts:
Node* previous = nullptr;
Node* current = head;
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | nullptr |
| current | 0x1000 |
| next | not set |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=2000 |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
head/current
|
v
1 -> 2 -> 3 -> null
previous -> null
At this point, nothing has been reversed yet.
Step 1 — Save next
Inside the first loop iteration:
Node* next = current->next;
current points to Node 1.
current->next points to Node 2.
So:
next = 0x2000
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | nullptr |
| current | 0x1000 |
| next | 0x2000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=2000 |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Nothing in the heap changed yet.
The next stack variable only saves access to the rest of the list.
Without this temporary pointer, Node 2 and Node 3 could be lost after the next operation.
Step 2 — Reverse current->next
Now the algorithm changes the link:
current->next = previous;
current is Node 1.
previous is nullptr.
So Node 1 no longer points to Node 2.
It now points to nullptr.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | nullptr |
| current | 0x1000 |
| next | 0x2000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
current
|
v
1 -> null
next
|
v
2 -> 3 -> null
This is the key mutation.
The original list is now split into two logical parts:
Reversed part:
1 -> null
Remaining original part:
2 -> 3 -> null
Step 3 — Move previous
The algorithm advances the reversed part:
previous = current;
previous now points to Node 1.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x1000 |
| current | 0x1000 |
| next | 0x2000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
previous/current
|
v
1 -> null
next
|
v
2 -> 3 -> null
The reversed part now officially starts at previous.
Step 4 — Move current
The algorithm continues with the saved next node:
current = next;
current now points to Node 2.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x1000 |
| current | 0x2000 |
| next | 0x2000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
previous
|
v
1 -> null
current
|
v
2 -> 3 -> null
The first iteration is complete.
Step 5 — Second Iteration: Save next
The loop repeats.
Node* next = current->next;
current points to Node 2.
Node 2 points to Node 3.
So:
next = 0x3000
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x1000 |
| current | 0x2000 |
| next | 0x3000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=3000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Again, the heap has not changed yet.
Step 6 — Second Iteration: Reverse Link
current->next = previous;
current is Node 2.
previous is Node 1.
So Node 2 now points back to Node 1.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x1000 |
| current | 0x2000 |
| next | 0x3000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
current
|
v
2 -> 1 -> null
next
|
v
3 -> null
The reversed part will become:
2 -> 1 -> null
after previous moves to Node 2.
Step 7 — Second Iteration: Move Pointers
previous = current;
current = next;
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x2000 |
| current | 0x3000 |
| next | 0x3000 |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Logical view
previous
|
v
2 -> 1 -> null
current
|
v
3 -> null
Now two nodes are reversed.
Step 8 — Third Iteration: Save next
Node* next = current->next;
current is Node 3.
Node 3 points to nullptr.
So:
next = nullptr
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x2000 |
| current | 0x3000 |
| next | nullptr |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=null |
+-----------+-----------+
Step 9 — Third Iteration: Reverse Link
current->next = previous;
current is Node 3.
previous is Node 2.
So Node 3 now points to Node 2.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x2000 |
| current | 0x3000 |
| next | nullptr |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=2000 |
+-----------+-----------+
Logical view
current
|
v
3 -> 2 -> 1 -> null
The whole list is now reversed, but the loop still needs to update the stack pointers.
Step 10 — Third Iteration: Move Pointers
previous = current;
current = next;
Since next is nullptr, current becomes nullptr.
Stack
+----------+----------+
| Variable | Value |
+----------+----------+
| head | 0x1000 |
| previous | 0x3000 |
| current | nullptr |
| next | nullptr |
+----------+----------+
Heap
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x3000
+-----------+-----------+
| value = 3 | next=2000 |
+-----------+-----------+
Logical view
previous
|
v
3 -> 2 -> 1 -> null
current -> null
The loop condition fails:
while(current != nullptr)
because current is now nullptr.
Step 11 — Return New Head
At the end:
return previous;
previous points to Node 3.
Node 3 is the new head of the reversed list.
Final stack view
+----------+----------+
| Variable | Value |
+----------+----------+
| old head | 0x1000 |
| new head | 0x3000 |
+----------+----------+
Final heap view
0x3000
+-----------+-----------+
| value = 3 | next=2000 |
+-----------+-----------+
0x2000
+-----------+-----------+
| value = 2 | next=1000 |
+-----------+-----------+
0x1000
+-----------+-----------+
| value = 1 | next=null |
+-----------+-----------+
Final logical view
new head
|
v
3 -> 2 -> 1 -> null
Why the Temporary next Pointer Matters
This line is not optional:
Node* next = current->next;
Without it, this operation:
current->next = previous;
would overwrite the only pointer to the remaining original list.
For example, at the beginning:
1 -> 2 -> 3 -> null
If Node 1 is changed to:
1 -> null
before saving Node 2, then Node 2 and Node 3 are no longer reachable from any local variable.
That is why the algorithm always follows this order:
Node* next = current->next; // preserve the remaining list
current->next = previous; // reverse the link
previous = current; // grow the reversed part
current = next; // continue with the remaining part
The order is the algorithm.
Summary
During the algorithm:
previouspoints to the already reversed part.currentpoints to the node being processed.nextpreserves access to the not-yet-processed part.- Only one
nextfield is modified per iteration. - No nodes are copied.
- No new list is allocated.
- The original nodes are relinked in-place.
The algorithm is small, but it is easy to get wrong because it mutates the structure while traversing it.
That is why a memory-level walkthrough is often more useful than just showing the final code.