Files
beyond-interviews/analysis/04-reverse-linked-list/exapmle/reverse-linked-list/memory_walkthrough.md

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
  • next fields 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 next before changing current->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.


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 |
+-----------+-----------+

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:

  • previous points to the already reversed part.
  • current points to the node being processed.
  • next preserves access to the not-yet-processed part.
  • Only one next field 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.