feat(patterns): pointer/array tutorials
This commit is contained in:
279
backend/data/patterns/linkedlist-reversal.yaml
Normal file
279
backend/data/patterns/linkedlist-reversal.yaml
Normal file
@@ -0,0 +1,279 @@
|
||||
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: []
|
||||
Reference in New Issue
Block a user