280 lines
7.8 KiB
YAML
280 lines
7.8 KiB
YAML
name: LinkedList In-Place Reversal
|
|
slug: linkedlist-reversal
|
|
difficulty_level: 2
|
|
|
|
description: >
|
|
Reverse linked list nodes in-place by manipulating pointers without allocating
|
|
extra space. This technique uses three pointers to track the previous, current,
|
|
and next nodes while systematically reversing the direction of links.
|
|
|
|
when_to_use: |
|
|
- Reversing an entire linked list
|
|
- Reversing a portion of a linked list
|
|
- Reversing in groups of K nodes
|
|
- Palindrome linked list verification
|
|
- Reordering list problems
|
|
|
|
metaphor: |
|
|
Imagine a conga line where everyone faces forward. To reverse it, you don't
|
|
rearrange people—you have each person turn around and grab the shoulders of
|
|
whoever was behind them. You process one person at a time: they turn around,
|
|
the next person steps forward, and so on until everyone faces the opposite direction.
|
|
|
|
Another analogy: reversing a chain of paper clips. You unclip each one from its
|
|
forward neighbor and clip it to its backward neighbor, working through the chain.
|
|
|
|
core_concept: |
|
|
Linked list reversal uses **three pointers** moving through the list:
|
|
|
|
- **prev**: Points to the already-reversed portion (starts as null)
|
|
- **curr**: The node currently being processed
|
|
- **next**: Temporarily stores the next node before we break the link
|
|
|
|
At each step:
|
|
1. Save `curr.next` in `next` (before we lose it)
|
|
2. Reverse the link: `curr.next = prev`
|
|
3. Advance: `prev = curr`, `curr = next`
|
|
|
|
The key insight is that we're not moving nodes—we're redirecting pointers.
|
|
This achieves O(n) time with O(1) space.
|
|
|
|
For **partial reversal** (reversing between positions m and n), we:
|
|
1. Navigate to position m-1 (the node before reversal starts)
|
|
2. Reverse nodes from m to n
|
|
3. Reconnect the reversed portion to the rest of the list
|
|
|
|
visualization: |
|
|
**Full list reversal:**
|
|
|
|
```
|
|
Initial: 1 → 2 → 3 → 4 → null
|
|
prev=null, curr=1
|
|
|
|
Step 1: Save next=2, reverse 1's link
|
|
null ← 1 2 → 3 → 4
|
|
prev curr
|
|
|
|
Step 2: Save next=3, reverse 2's link
|
|
null ← 1 ← 2 3 → 4
|
|
prev curr
|
|
|
|
Step 3: Save next=4, reverse 3's link
|
|
null ← 1 ← 2 ← 3 4
|
|
prev curr
|
|
|
|
Step 4: Save next=null, reverse 4's link
|
|
null ← 1 ← 2 ← 3 ← 4
|
|
prev curr=null
|
|
|
|
Result: 4 → 3 → 2 → 1 → null
|
|
```
|
|
|
|
**Partial reversal (positions 2 to 4):**
|
|
|
|
```
|
|
Initial: 1 → 2 → 3 → 4 → 5
|
|
positions: 1 2 3 4 5
|
|
|
|
Goal: 1 → 4 → 3 → 2 → 5
|
|
|
|
Step 1: Find node before position 2
|
|
before = node 1
|
|
|
|
Step 2: Reverse nodes 2, 3, 4
|
|
1 null ← 2 ← 3 ← 4 5
|
|
↑ ↑ ↑
|
|
before prev curr
|
|
|
|
Step 3: Reconnect
|
|
before.next.next = curr (2 → 5)
|
|
before.next = prev (1 → 4)
|
|
|
|
Result: 1 → 4 → 3 → 2 → 5
|
|
```
|
|
|
|
code_template: |
|
|
class ListNode:
|
|
def __init__(self, val=0, next=None):
|
|
self.val = val
|
|
self.next = next
|
|
|
|
|
|
def reverse_list(head: ListNode) -> ListNode:
|
|
"""Reverse entire linked list."""
|
|
prev = None
|
|
curr = head
|
|
|
|
while curr:
|
|
next_node = curr.next # Save next
|
|
curr.next = prev # Reverse link
|
|
prev = curr # Advance prev
|
|
curr = next_node # Advance curr
|
|
|
|
return prev # New head
|
|
|
|
|
|
def reverse_between(head: ListNode, m: int, n: int) -> ListNode:
|
|
"""Reverse nodes from position m to n (1-indexed)."""
|
|
if not head or m == n:
|
|
return head
|
|
|
|
dummy = ListNode(0)
|
|
dummy.next = head
|
|
before = dummy
|
|
|
|
# Move to node before reversal starts
|
|
for _ in range(m - 1):
|
|
before = before.next
|
|
|
|
# Reverse n - m + 1 nodes
|
|
prev = None
|
|
curr = before.next
|
|
for _ in range(n - m + 1):
|
|
next_node = curr.next
|
|
curr.next = prev
|
|
prev = curr
|
|
curr = next_node
|
|
|
|
# Reconnect
|
|
before.next.next = curr # tail of reversed → rest of list
|
|
before.next = prev # before → new head of reversed
|
|
|
|
return dummy.next
|
|
|
|
|
|
def reverse_k_group(head: ListNode, k: int) -> ListNode:
|
|
"""Reverse nodes in groups of k."""
|
|
# Count total nodes
|
|
count = 0
|
|
node = head
|
|
while node:
|
|
count += 1
|
|
node = node.next
|
|
|
|
dummy = ListNode(0)
|
|
dummy.next = head
|
|
before = dummy
|
|
|
|
while count >= k:
|
|
# Reverse k nodes
|
|
prev = None
|
|
curr = before.next
|
|
for _ in range(k):
|
|
next_node = curr.next
|
|
curr.next = prev
|
|
prev = curr
|
|
curr = next_node
|
|
|
|
# Reconnect
|
|
tail = before.next
|
|
tail.next = curr
|
|
before.next = prev
|
|
|
|
# Move to next group
|
|
before = tail
|
|
count -= k
|
|
|
|
return dummy.next
|
|
|
|
|
|
def reverse_list_recursive(head: ListNode) -> ListNode:
|
|
"""Reverse list using recursion."""
|
|
if not head or not head.next:
|
|
return head
|
|
|
|
new_head = reverse_list_recursive(head.next)
|
|
head.next.next = head # Reverse link
|
|
head.next = None # Prevent cycle
|
|
|
|
return new_head
|
|
|
|
recognition_signals:
|
|
- "reverse linked list"
|
|
- "reverse between"
|
|
- "reverse in groups"
|
|
- "reverse k-group"
|
|
- "palindrome linked list"
|
|
- "reorder list"
|
|
- "swap nodes"
|
|
- "rotate list"
|
|
|
|
common_mistakes:
|
|
- title: Losing reference to next node
|
|
description: |
|
|
Reversing `curr.next` before saving it means you can't advance to the
|
|
next node.
|
|
fix: |
|
|
Always save the next node first:
|
|
```python
|
|
next_node = curr.next # Save FIRST
|
|
curr.next = prev # Then reverse
|
|
```
|
|
|
|
- title: Forgetting to update connections in partial reversal
|
|
description: |
|
|
Reversing the middle portion without reconnecting it to the beginning
|
|
and end of the list breaks the list.
|
|
fix: |
|
|
After reversing, reconnect both ends:
|
|
```python
|
|
before.next.next = curr # tail → rest
|
|
before.next = prev # before → new head
|
|
```
|
|
|
|
- title: Not using a dummy node
|
|
description: |
|
|
When the reversal might include the head, handling the head separately
|
|
adds complexity and edge cases.
|
|
fix: |
|
|
Use a dummy node pointing to head. This simplifies edge cases:
|
|
```python
|
|
dummy = ListNode(0)
|
|
dummy.next = head
|
|
# ... reversal logic ...
|
|
return dummy.next
|
|
```
|
|
|
|
- title: Off-by-one with positions
|
|
description: |
|
|
Confusing 0-indexed vs 1-indexed positions causes reversal of wrong nodes.
|
|
fix: |
|
|
Clarify indexing convention. For 1-indexed positions, loop `m-1` times
|
|
to reach the node *before* position m.
|
|
|
|
variations:
|
|
- name: Full reversal
|
|
description: |
|
|
Reverse the entire linked list. Simplest form—just walk through and
|
|
reverse each link.
|
|
example: "Reverse Linked List"
|
|
|
|
- name: Partial reversal
|
|
description: |
|
|
Reverse only nodes between positions m and n. Need to track connection
|
|
points before and after the reversed section.
|
|
example: "Reverse Linked List II"
|
|
|
|
- name: K-group reversal
|
|
description: |
|
|
Reverse every k consecutive nodes. Often requires counting total nodes
|
|
first to know when to stop.
|
|
example: "Reverse Nodes in k-Group"
|
|
|
|
- name: Alternating reversal
|
|
description: |
|
|
Reverse every other group of k nodes. Combines k-group logic with
|
|
skip logic.
|
|
example: "Reverse Alternate K Nodes"
|
|
|
|
- name: Recursive reversal
|
|
description: |
|
|
Elegant recursive solution that reverses by relying on the recursive
|
|
call to reverse the rest, then fixing up the current node.
|
|
example: "Reverse Linked List (recursive approach)"
|
|
|
|
related_patterns:
|
|
- fast-slow-pointers
|
|
- two-pointers
|
|
|
|
prerequisite_patterns: []
|