Ref: 2 memory walkthrough was added
This commit is contained in:
@@ -23,3 +23,8 @@ Original list:
|
||||
Reversed list:
|
||||
5 -> 4 -> 3 -> 2 -> 1 -> null
|
||||
```
|
||||
|
||||
|
||||
## Memory walkthrough
|
||||
|
||||
see memory_walkthrough.md
|
||||
@@ -0,0 +1,827 @@
|
||||
# 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:
|
||||
|
||||
```text
|
||||
1 -> 2 -> 3 -> null
|
||||
```
|
||||
|
||||
For clarity, fake addresses are used:
|
||||
|
||||
```text
|
||||
Node 1: 0x1000
|
||||
Node 2: 0x2000
|
||||
Node 3: 0x3000
|
||||
```
|
||||
|
||||
These addresses are illustrative only.
|
||||
A real program will use different addresses.
|
||||
|
||||
---
|
||||
|
||||
## Algorithm
|
||||
|
||||
```cpp
|
||||
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:
|
||||
|
||||
```cpp
|
||||
Node* previous = nullptr;
|
||||
Node* current = head;
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | nullptr |
|
||||
| current | 0x1000 |
|
||||
| next | not set |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=2000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=3000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```cpp
|
||||
Node* next = current->next;
|
||||
```
|
||||
|
||||
`current` points to Node 1.
|
||||
`current->next` points to Node 2.
|
||||
|
||||
So:
|
||||
|
||||
```text
|
||||
next = 0x2000
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | nullptr |
|
||||
| current | 0x1000 |
|
||||
| next | 0x2000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | nullptr |
|
||||
| current | 0x1000 |
|
||||
| next | 0x2000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=3000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
current
|
||||
|
|
||||
v
|
||||
1 -> null
|
||||
|
||||
next
|
||||
|
|
||||
v
|
||||
2 -> 3 -> null
|
||||
```
|
||||
|
||||
This is the key mutation.
|
||||
|
||||
The original list is now split into two logical parts:
|
||||
|
||||
```text
|
||||
Reversed part:
|
||||
1 -> null
|
||||
|
||||
Remaining original part:
|
||||
2 -> 3 -> null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Move `previous`
|
||||
|
||||
The algorithm advances the reversed part:
|
||||
|
||||
```cpp
|
||||
previous = current;
|
||||
```
|
||||
|
||||
`previous` now points to Node 1.
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x1000 |
|
||||
| current | 0x1000 |
|
||||
| next | 0x2000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=3000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```cpp
|
||||
current = next;
|
||||
```
|
||||
|
||||
`current` now points to Node 2.
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x1000 |
|
||||
| current | 0x2000 |
|
||||
| next | 0x2000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=3000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
previous
|
||||
|
|
||||
v
|
||||
1 -> null
|
||||
|
||||
current
|
||||
|
|
||||
v
|
||||
2 -> 3 -> null
|
||||
```
|
||||
|
||||
The first iteration is complete.
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Second Iteration: Save `next`
|
||||
|
||||
The loop repeats.
|
||||
|
||||
```cpp
|
||||
Node* next = current->next;
|
||||
```
|
||||
|
||||
`current` points to Node 2.
|
||||
Node 2 points to Node 3.
|
||||
|
||||
So:
|
||||
|
||||
```text
|
||||
next = 0x3000
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x1000 |
|
||||
| current | 0x2000 |
|
||||
| next | 0x3000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
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
|
||||
|
||||
```cpp
|
||||
current->next = previous;
|
||||
```
|
||||
|
||||
`current` is Node 2.
|
||||
`previous` is Node 1.
|
||||
|
||||
So Node 2 now points back to Node 1.
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x1000 |
|
||||
| current | 0x2000 |
|
||||
| next | 0x3000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
current
|
||||
|
|
||||
v
|
||||
2 -> 1 -> null
|
||||
|
||||
next
|
||||
|
|
||||
v
|
||||
3 -> null
|
||||
```
|
||||
|
||||
The reversed part will become:
|
||||
|
||||
```text
|
||||
2 -> 1 -> null
|
||||
```
|
||||
|
||||
after `previous` moves to Node 2.
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — Second Iteration: Move Pointers
|
||||
|
||||
```cpp
|
||||
previous = current;
|
||||
current = next;
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x2000 |
|
||||
| current | 0x3000 |
|
||||
| next | 0x3000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
previous
|
||||
|
|
||||
v
|
||||
2 -> 1 -> null
|
||||
|
||||
current
|
||||
|
|
||||
v
|
||||
3 -> null
|
||||
```
|
||||
|
||||
Now two nodes are reversed.
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — Third Iteration: Save `next`
|
||||
|
||||
```cpp
|
||||
Node* next = current->next;
|
||||
```
|
||||
|
||||
`current` is Node 3.
|
||||
Node 3 points to `nullptr`.
|
||||
|
||||
So:
|
||||
|
||||
```text
|
||||
next = nullptr
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x2000 |
|
||||
| current | 0x3000 |
|
||||
| next | nullptr |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 9 — Third Iteration: Reverse Link
|
||||
|
||||
```cpp
|
||||
current->next = previous;
|
||||
```
|
||||
|
||||
`current` is Node 3.
|
||||
`previous` is Node 2.
|
||||
|
||||
So Node 3 now points to Node 2.
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x2000 |
|
||||
| current | 0x3000 |
|
||||
| next | nullptr |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=2000 |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
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
|
||||
|
||||
```cpp
|
||||
previous = current;
|
||||
current = next;
|
||||
```
|
||||
|
||||
Since `next` is `nullptr`, `current` becomes `nullptr`.
|
||||
|
||||
### Stack
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| head | 0x1000 |
|
||||
| previous | 0x3000 |
|
||||
| current | nullptr |
|
||||
| next | nullptr |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Heap
|
||||
|
||||
```text
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=2000 |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Logical view
|
||||
|
||||
```text
|
||||
previous
|
||||
|
|
||||
v
|
||||
3 -> 2 -> 1 -> null
|
||||
|
||||
current -> null
|
||||
```
|
||||
|
||||
The loop condition fails:
|
||||
|
||||
```cpp
|
||||
while(current != nullptr)
|
||||
```
|
||||
|
||||
because `current` is now `nullptr`.
|
||||
|
||||
---
|
||||
|
||||
## Step 11 — Return New Head
|
||||
|
||||
At the end:
|
||||
|
||||
```cpp
|
||||
return previous;
|
||||
```
|
||||
|
||||
`previous` points to Node 3.
|
||||
|
||||
Node 3 is the new head of the reversed list.
|
||||
|
||||
### Final stack view
|
||||
|
||||
```text
|
||||
+----------+----------+
|
||||
| Variable | Value |
|
||||
+----------+----------+
|
||||
| old head | 0x1000 |
|
||||
| new head | 0x3000 |
|
||||
+----------+----------+
|
||||
```
|
||||
|
||||
### Final heap view
|
||||
|
||||
```text
|
||||
0x3000
|
||||
+-----------+-----------+
|
||||
| value = 3 | next=2000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x2000
|
||||
+-----------+-----------+
|
||||
| value = 2 | next=1000 |
|
||||
+-----------+-----------+
|
||||
|
||||
0x1000
|
||||
+-----------+-----------+
|
||||
| value = 1 | next=null |
|
||||
+-----------+-----------+
|
||||
```
|
||||
|
||||
### Final logical view
|
||||
|
||||
```text
|
||||
new head
|
||||
|
|
||||
v
|
||||
3 -> 2 -> 1 -> null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why the Temporary `next` Pointer Matters
|
||||
|
||||
This line is not optional:
|
||||
|
||||
```cpp
|
||||
Node* next = current->next;
|
||||
```
|
||||
|
||||
Without it, this operation:
|
||||
|
||||
```cpp
|
||||
current->next = previous;
|
||||
```
|
||||
|
||||
would overwrite the only pointer to the remaining original list.
|
||||
|
||||
For example, at the beginning:
|
||||
|
||||
```text
|
||||
1 -> 2 -> 3 -> null
|
||||
```
|
||||
|
||||
If Node 1 is changed to:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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.
|
||||
Reference in New Issue
Block a user