feat(patterns): graph/tree traversal tutorials
This commit is contained in:
255
backend/data/patterns/bfs.yaml
Normal file
255
backend/data/patterns/bfs.yaml
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
name: BFS (Breadth-First Search)
|
||||||
|
slug: bfs
|
||||||
|
difficulty_level: 3
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Level-by-level traversal using a queue, exploring all neighbors at the current
|
||||||
|
depth before moving deeper. This guarantees the shortest path in unweighted
|
||||||
|
graphs and is ideal for problems requiring layer-by-layer processing.
|
||||||
|
|
||||||
|
when_to_use: |
|
||||||
|
- Shortest path in unweighted graphs
|
||||||
|
- Level-order tree traversal
|
||||||
|
- Finding all nodes at distance K
|
||||||
|
- Word ladder / transformation problems
|
||||||
|
- Spreading simulations (rotting oranges, infection)
|
||||||
|
|
||||||
|
metaphor: |
|
||||||
|
Imagine dropping a stone into a still pond. Ripples spread outward in concentric
|
||||||
|
circles—first touching everything 1 meter away, then 2 meters, then 3 meters.
|
||||||
|
BFS works the same way: it explores all nodes at distance 1 before any node at
|
||||||
|
distance 2.
|
||||||
|
|
||||||
|
Another analogy: searching for someone in a building. You check everyone on your
|
||||||
|
current floor before taking the stairs to the next floor. You never go upstairs
|
||||||
|
until you've searched every room on the current level.
|
||||||
|
|
||||||
|
core_concept: |
|
||||||
|
BFS uses a **queue** (FIFO) to process nodes in the order they were discovered.
|
||||||
|
This guarantees that when you first reach a node, you've taken the shortest path
|
||||||
|
to get there (in terms of number of edges).
|
||||||
|
|
||||||
|
The key insight is that the queue naturally separates nodes by their distance
|
||||||
|
from the source. Everything added in round 1 is at distance 1. Everything added
|
||||||
|
in round 2 is at distance 2. By the time you dequeue a node, all closer nodes
|
||||||
|
have already been processed.
|
||||||
|
|
||||||
|
**Why it finds shortest paths**: If there were a shorter path to node X, you
|
||||||
|
would have discovered X earlier (through that shorter path) and already
|
||||||
|
processed it. The first time you reach X is always via the shortest route.
|
||||||
|
|
||||||
|
visualization: |
|
||||||
|
**Example: Shortest path from A to F**
|
||||||
|
|
||||||
|
```
|
||||||
|
Graph:
|
||||||
|
A --- B --- E
|
||||||
|
| |
|
||||||
|
C --- D --- F
|
||||||
|
|
||||||
|
BFS from A:
|
||||||
|
|
||||||
|
Queue: [A] Visited: {A} Level 0
|
||||||
|
Process A → add B, C
|
||||||
|
|
||||||
|
Queue: [B, C] Visited: {A,B,C} Level 1
|
||||||
|
Process B → add E, D
|
||||||
|
Process C → add D (skip, visited)
|
||||||
|
|
||||||
|
Queue: [E, D] Visited: {A,B,C,E,D} Level 2
|
||||||
|
Process E → no new neighbors
|
||||||
|
Process D → add F
|
||||||
|
|
||||||
|
Queue: [F] Visited: {A,B,C,E,D,F} Level 3
|
||||||
|
Process F → Found! Distance = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level-order tree traversal:**
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
/ \
|
||||||
|
2 3
|
||||||
|
/ \ \
|
||||||
|
4 5 6
|
||||||
|
|
||||||
|
Level 0: [1]
|
||||||
|
Level 1: [2, 3]
|
||||||
|
Level 2: [4, 5, 6]
|
||||||
|
|
||||||
|
Result: [[1], [2,3], [4,5,6]]
|
||||||
|
```
|
||||||
|
|
||||||
|
code_template: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
def bfs_shortest_path(graph: dict, start: str, end: str) -> int:
|
||||||
|
"""Find shortest path distance in unweighted graph."""
|
||||||
|
if start == end:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
queue = deque([start])
|
||||||
|
visited = {start}
|
||||||
|
distance = 0
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
distance += 1
|
||||||
|
# Process all nodes at current level
|
||||||
|
for _ in range(len(queue)):
|
||||||
|
node = queue.popleft()
|
||||||
|
|
||||||
|
for neighbor in graph[node]:
|
||||||
|
if neighbor == end:
|
||||||
|
return distance
|
||||||
|
if neighbor not in visited:
|
||||||
|
visited.add(neighbor)
|
||||||
|
queue.append(neighbor)
|
||||||
|
|
||||||
|
return -1 # No path found
|
||||||
|
|
||||||
|
|
||||||
|
def bfs_level_order(root) -> list[list]:
|
||||||
|
"""Level-order traversal of binary tree."""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
queue = deque([root])
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
level = []
|
||||||
|
for _ in range(len(queue)):
|
||||||
|
node = queue.popleft()
|
||||||
|
level.append(node.val)
|
||||||
|
|
||||||
|
if node.left:
|
||||||
|
queue.append(node.left)
|
||||||
|
if node.right:
|
||||||
|
queue.append(node.right)
|
||||||
|
|
||||||
|
result.append(level)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def bfs_matrix(grid: list[list[int]], start: tuple) -> int:
|
||||||
|
"""BFS on a 2D grid."""
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
queue = deque([start])
|
||||||
|
visited = {start}
|
||||||
|
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
|
||||||
|
distance = 0
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
for _ in range(len(queue)):
|
||||||
|
r, c = queue.popleft()
|
||||||
|
|
||||||
|
for dr, dc in directions:
|
||||||
|
nr, nc = r + dr, c + dc
|
||||||
|
if (0 <= nr < rows and 0 <= nc < cols
|
||||||
|
and (nr, nc) not in visited
|
||||||
|
and grid[nr][nc] != 0): # 0 = wall
|
||||||
|
visited.add((nr, nc))
|
||||||
|
queue.append((nr, nc))
|
||||||
|
|
||||||
|
distance += 1
|
||||||
|
|
||||||
|
return distance
|
||||||
|
|
||||||
|
recognition_signals:
|
||||||
|
- "shortest path"
|
||||||
|
- "minimum steps"
|
||||||
|
- "level order"
|
||||||
|
- "nearest"
|
||||||
|
- "unweighted graph"
|
||||||
|
- "distance"
|
||||||
|
- "spreading"
|
||||||
|
- "layer by layer"
|
||||||
|
- "all nodes at distance K"
|
||||||
|
- "word ladder"
|
||||||
|
- "rotting oranges"
|
||||||
|
|
||||||
|
common_mistakes:
|
||||||
|
- title: Adding to visited after dequeuing instead of when enqueuing
|
||||||
|
description: |
|
||||||
|
Marking nodes as visited when you dequeue them (instead of when you
|
||||||
|
enqueue them) causes the same node to be added multiple times, leading
|
||||||
|
to incorrect distances and wasted processing.
|
||||||
|
fix: |
|
||||||
|
Always mark nodes as visited immediately when adding to the queue:
|
||||||
|
```python
|
||||||
|
if neighbor not in visited:
|
||||||
|
visited.add(neighbor) # Mark NOW
|
||||||
|
queue.append(neighbor)
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Not tracking levels correctly
|
||||||
|
description: |
|
||||||
|
Processing the entire queue without tracking level boundaries makes it
|
||||||
|
impossible to know the distance or handle level-by-level logic.
|
||||||
|
fix: |
|
||||||
|
Use the "process current level size" pattern:
|
||||||
|
```python
|
||||||
|
for _ in range(len(queue)): # Fixed size at level start
|
||||||
|
node = queue.popleft()
|
||||||
|
# Process node
|
||||||
|
distance += 1 # Increment after level completes
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Using a list instead of deque
|
||||||
|
description: |
|
||||||
|
Using `list.pop(0)` is O(n) because all elements must shift. This turns
|
||||||
|
O(V+E) BFS into O(V²).
|
||||||
|
fix: |
|
||||||
|
Use `collections.deque` which has O(1) popleft:
|
||||||
|
```python
|
||||||
|
from collections import deque
|
||||||
|
queue = deque([start])
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Forgetting to check bounds in grid BFS
|
||||||
|
description: |
|
||||||
|
Moving to invalid coordinates (negative or beyond grid dimensions) causes
|
||||||
|
index errors.
|
||||||
|
fix: |
|
||||||
|
Always validate coordinates before accessing the grid:
|
||||||
|
```python
|
||||||
|
if 0 <= nr < rows and 0 <= nc < cols:
|
||||||
|
# Safe to access grid[nr][nc]
|
||||||
|
```
|
||||||
|
|
||||||
|
variations:
|
||||||
|
- name: Shortest path (unweighted)
|
||||||
|
description: |
|
||||||
|
Classic BFS to find minimum number of edges between two nodes.
|
||||||
|
example: "Word Ladder, Minimum Genetic Mutation, Open the Lock"
|
||||||
|
|
||||||
|
- name: Level-order traversal
|
||||||
|
description: |
|
||||||
|
Process tree nodes level by level, returning grouped results.
|
||||||
|
example: "Binary Tree Level Order Traversal, Zigzag Level Order"
|
||||||
|
|
||||||
|
- name: Multi-source BFS
|
||||||
|
description: |
|
||||||
|
Start BFS from multiple sources simultaneously. Useful for "spreading"
|
||||||
|
problems where multiple starting points expand in parallel.
|
||||||
|
example: "Rotting Oranges, Walls and Gates, 01 Matrix"
|
||||||
|
|
||||||
|
- name: Bidirectional BFS
|
||||||
|
description: |
|
||||||
|
Search from both start and end simultaneously, meeting in the middle.
|
||||||
|
Reduces search space from O(b^d) to O(b^(d/2)).
|
||||||
|
example: "Word Ladder (optimized), Shortest Path in large graphs"
|
||||||
|
|
||||||
|
- name: BFS with state
|
||||||
|
description: |
|
||||||
|
Track additional state beyond position (e.g., keys collected, walls broken).
|
||||||
|
State becomes part of the "node" being visited.
|
||||||
|
example: "Shortest Path in Grid with Obstacles Elimination"
|
||||||
|
|
||||||
|
related_patterns:
|
||||||
|
- dfs
|
||||||
|
- tree-traversal
|
||||||
|
- matrix-traversal
|
||||||
|
|
||||||
|
prerequisite_patterns: []
|
||||||
275
backend/data/patterns/dfs.yaml
Normal file
275
backend/data/patterns/dfs.yaml
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
name: DFS (Depth-First Search)
|
||||||
|
slug: dfs
|
||||||
|
difficulty_level: 3
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Explore as deep as possible along each branch before backtracking, using
|
||||||
|
recursion or an explicit stack. DFS is memory-efficient and naturally suited
|
||||||
|
for problems involving paths, connectivity, and exhaustive exploration.
|
||||||
|
|
||||||
|
when_to_use: |
|
||||||
|
- Finding any path (not necessarily shortest)
|
||||||
|
- Detecting cycles in graphs
|
||||||
|
- Topological sorting
|
||||||
|
- Connected components
|
||||||
|
- Tree traversals (preorder, inorder, postorder)
|
||||||
|
- Exhaustive search when you need to explore all possibilities
|
||||||
|
|
||||||
|
metaphor: |
|
||||||
|
Imagine exploring a maze by always taking the first available turn until you
|
||||||
|
hit a dead end. Then you backtrack to the last intersection and try the next
|
||||||
|
option. You keep going deeper until stuck, then retreat and try alternatives.
|
||||||
|
|
||||||
|
Another analogy: reading a "choose your own adventure" book. You follow one
|
||||||
|
story path all the way to its ending, then go back and explore different
|
||||||
|
choices you didn't take.
|
||||||
|
|
||||||
|
core_concept: |
|
||||||
|
DFS uses a **stack** (LIFO)—either the call stack via recursion or an explicit
|
||||||
|
stack data structure. This naturally explores depth-first: the most recently
|
||||||
|
discovered node is explored first.
|
||||||
|
|
||||||
|
The key insight is that DFS maintains the current path from root to current
|
||||||
|
node on the stack. This makes it perfect for:
|
||||||
|
|
||||||
|
1. **Path problems**: The current path is always available during recursion
|
||||||
|
2. **Cycle detection**: If you encounter a node already on the current path,
|
||||||
|
there's a cycle
|
||||||
|
3. **Backtracking**: Naturally undo choices when returning from recursion
|
||||||
|
|
||||||
|
Unlike BFS, DFS doesn't guarantee shortest paths, but it uses O(h) memory
|
||||||
|
(height of tree/recursion depth) vs BFS's O(w) (width of level).
|
||||||
|
|
||||||
|
visualization: |
|
||||||
|
**Example: DFS traversal of graph**
|
||||||
|
|
||||||
|
```
|
||||||
|
Graph:
|
||||||
|
A --- B --- E
|
||||||
|
| |
|
||||||
|
C --- D --- F
|
||||||
|
|
||||||
|
DFS from A (alphabetical order):
|
||||||
|
|
||||||
|
Visit A → explore neighbors
|
||||||
|
Visit B → explore neighbors
|
||||||
|
Visit D → explore neighbors
|
||||||
|
Visit C (already visited? skip or detect cycle)
|
||||||
|
Visit F → no unvisited neighbors, backtrack
|
||||||
|
Backtrack to D, backtrack to B
|
||||||
|
Visit E → no unvisited neighbors, backtrack
|
||||||
|
Backtrack to B, backtrack to A
|
||||||
|
Visit C → D already visited, backtrack
|
||||||
|
Done
|
||||||
|
|
||||||
|
Order visited: A → B → D → F → E → C
|
||||||
|
```
|
||||||
|
|
||||||
|
**Recursion call stack visualization:**
|
||||||
|
|
||||||
|
```
|
||||||
|
dfs(A)
|
||||||
|
└─ dfs(B)
|
||||||
|
└─ dfs(D)
|
||||||
|
└─ dfs(F) ← returns
|
||||||
|
└─ dfs(C) ← already visited
|
||||||
|
└─ dfs(E) ← returns
|
||||||
|
└─ dfs(C) ← already visited
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cycle detection with colors:**
|
||||||
|
|
||||||
|
```
|
||||||
|
WHITE (0) = unvisited
|
||||||
|
GRAY (1) = in current path (on stack)
|
||||||
|
BLACK (2) = fully processed
|
||||||
|
|
||||||
|
If we visit a GRAY node → cycle detected!
|
||||||
|
```
|
||||||
|
|
||||||
|
code_template: |
|
||||||
|
def dfs_recursive(graph: dict, start: str, visited: set = None) -> list:
|
||||||
|
"""Basic recursive DFS traversal."""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
visited.add(start)
|
||||||
|
result = [start]
|
||||||
|
|
||||||
|
for neighbor in graph[start]:
|
||||||
|
if neighbor not in visited:
|
||||||
|
result.extend(dfs_recursive(graph, neighbor, visited))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def dfs_iterative(graph: dict, start: str) -> list:
|
||||||
|
"""Iterative DFS using explicit stack."""
|
||||||
|
visited = set()
|
||||||
|
stack = [start]
|
||||||
|
result = []
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
if node in visited:
|
||||||
|
continue
|
||||||
|
|
||||||
|
visited.add(node)
|
||||||
|
result.append(node)
|
||||||
|
|
||||||
|
# Add neighbors in reverse for same order as recursive
|
||||||
|
for neighbor in reversed(graph[node]):
|
||||||
|
if neighbor not in visited:
|
||||||
|
stack.append(neighbor)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def dfs_path_finding(graph: dict, start: str, end: str) -> list:
|
||||||
|
"""Find a path from start to end using DFS."""
|
||||||
|
def dfs(node: str, path: list) -> list:
|
||||||
|
if node == end:
|
||||||
|
return path
|
||||||
|
|
||||||
|
for neighbor in graph[node]:
|
||||||
|
if neighbor not in path: # Avoid cycles
|
||||||
|
result = dfs(neighbor, path + [neighbor])
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
return dfs(start, [start])
|
||||||
|
|
||||||
|
|
||||||
|
def detect_cycle(graph: dict) -> bool:
|
||||||
|
"""Detect cycle in directed graph using DFS."""
|
||||||
|
WHITE, GRAY, BLACK = 0, 1, 2
|
||||||
|
color = {node: WHITE for node in graph}
|
||||||
|
|
||||||
|
def dfs(node: str) -> bool:
|
||||||
|
color[node] = GRAY # Mark as being processed
|
||||||
|
|
||||||
|
for neighbor in graph[node]:
|
||||||
|
if color[neighbor] == GRAY: # Back edge = cycle
|
||||||
|
return True
|
||||||
|
if color[neighbor] == WHITE and dfs(neighbor):
|
||||||
|
return True
|
||||||
|
|
||||||
|
color[node] = BLACK # Mark as complete
|
||||||
|
return False
|
||||||
|
|
||||||
|
return any(color[node] == WHITE and dfs(node) for node in graph)
|
||||||
|
|
||||||
|
|
||||||
|
def topological_sort(graph: dict) -> list:
|
||||||
|
"""Topological sort using DFS (Kahn's algorithm alternative)."""
|
||||||
|
visited = set()
|
||||||
|
result = []
|
||||||
|
|
||||||
|
def dfs(node: str):
|
||||||
|
visited.add(node)
|
||||||
|
for neighbor in graph.get(node, []):
|
||||||
|
if neighbor not in visited:
|
||||||
|
dfs(neighbor)
|
||||||
|
result.append(node) # Add after all descendants
|
||||||
|
|
||||||
|
for node in graph:
|
||||||
|
if node not in visited:
|
||||||
|
dfs(node)
|
||||||
|
|
||||||
|
return result[::-1] # Reverse for topological order
|
||||||
|
|
||||||
|
recognition_signals:
|
||||||
|
- "find path"
|
||||||
|
- "detect cycle"
|
||||||
|
- "connected components"
|
||||||
|
- "topological sort"
|
||||||
|
- "all paths"
|
||||||
|
- "explore all"
|
||||||
|
- "tree traversal"
|
||||||
|
- "preorder"
|
||||||
|
- "postorder"
|
||||||
|
- "inorder"
|
||||||
|
- "number of islands"
|
||||||
|
- "flood fill"
|
||||||
|
|
||||||
|
common_mistakes:
|
||||||
|
- title: Stack overflow with deep recursion
|
||||||
|
description: |
|
||||||
|
Recursive DFS on large graphs or trees can exceed Python's default
|
||||||
|
recursion limit (usually 1000), causing a stack overflow.
|
||||||
|
fix: |
|
||||||
|
Either increase the limit with `sys.setrecursionlimit(10000)` or convert
|
||||||
|
to iterative DFS with an explicit stack. Iterative is safer for unknown
|
||||||
|
input sizes.
|
||||||
|
|
||||||
|
- title: Marking visited too late
|
||||||
|
description: |
|
||||||
|
In iterative DFS, checking visited only when popping (not when pushing)
|
||||||
|
allows the same node to be added multiple times, wasting memory.
|
||||||
|
fix: |
|
||||||
|
For iterative DFS, either check when popping (simpler but less efficient)
|
||||||
|
or mark visited when pushing (more efficient):
|
||||||
|
```python
|
||||||
|
if neighbor not in visited:
|
||||||
|
visited.add(neighbor) # Mark when adding
|
||||||
|
stack.append(neighbor)
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Confusing visited vs current path
|
||||||
|
description: |
|
||||||
|
For cycle detection in directed graphs, using a simple visited set doesn't
|
||||||
|
distinguish between "node in current path" (cycle) and "node fully processed"
|
||||||
|
(not a cycle, just already explored).
|
||||||
|
fix: |
|
||||||
|
Use three states (WHITE/GRAY/BLACK) or track the current path separately.
|
||||||
|
A cycle exists only if you revisit a node that's still on the current path.
|
||||||
|
|
||||||
|
- title: Wrong order in topological sort
|
||||||
|
description: |
|
||||||
|
Adding nodes to result before processing descendants gives reverse
|
||||||
|
topological order or incorrect order entirely.
|
||||||
|
fix: |
|
||||||
|
Add nodes to result only after all descendants are processed (post-order).
|
||||||
|
Then reverse the result at the end.
|
||||||
|
|
||||||
|
variations:
|
||||||
|
- name: Tree DFS (preorder/inorder/postorder)
|
||||||
|
description: |
|
||||||
|
Visit nodes in specific orders relative to processing children. Preorder
|
||||||
|
visits parent first, inorder visits between children, postorder visits
|
||||||
|
parent last.
|
||||||
|
example: "Binary Tree Inorder Traversal, Serialize/Deserialize BST"
|
||||||
|
|
||||||
|
- name: Graph DFS
|
||||||
|
description: |
|
||||||
|
Traverse all reachable nodes from a starting point. Need to track visited
|
||||||
|
nodes to avoid infinite loops in cyclic graphs.
|
||||||
|
example: "Number of Islands, Clone Graph, Course Schedule"
|
||||||
|
|
||||||
|
- name: DFS with backtracking
|
||||||
|
description: |
|
||||||
|
Explore paths and undo choices when they don't lead to a solution. The
|
||||||
|
natural call stack provides the backtracking mechanism.
|
||||||
|
example: "N-Queens, Sudoku Solver, Permutations"
|
||||||
|
|
||||||
|
- name: DFS with memoization
|
||||||
|
description: |
|
||||||
|
Cache results of DFS from each node to avoid recomputation. Transforms
|
||||||
|
DFS into dynamic programming for certain problems.
|
||||||
|
example: "Longest Increasing Path in Matrix, Word Break"
|
||||||
|
|
||||||
|
- name: Iterative deepening DFS
|
||||||
|
description: |
|
||||||
|
Run DFS with increasing depth limits. Combines BFS's shortest-path
|
||||||
|
guarantee with DFS's memory efficiency.
|
||||||
|
example: "Finding shortest path when memory is limited"
|
||||||
|
|
||||||
|
related_patterns:
|
||||||
|
- bfs
|
||||||
|
- backtracking
|
||||||
|
- tree-traversal
|
||||||
|
- matrix-traversal
|
||||||
|
|
||||||
|
prerequisite_patterns: []
|
||||||
299
backend/data/patterns/matrix-traversal.yaml
Normal file
299
backend/data/patterns/matrix-traversal.yaml
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
name: Matrix Traversal
|
||||||
|
slug: matrix-traversal
|
||||||
|
difficulty_level: 3
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Navigate 2D grids systematically using DFS, BFS, or directional iteration.
|
||||||
|
Matrix traversal combines graph traversal concepts with coordinate-based
|
||||||
|
movement, treating each cell as a node connected to its neighbors.
|
||||||
|
|
||||||
|
when_to_use: |
|
||||||
|
- Finding connected regions (islands)
|
||||||
|
- Shortest path in a grid
|
||||||
|
- Flood fill / painting
|
||||||
|
- Spreading simulations (rotting oranges, fire spread)
|
||||||
|
- Word search in character grid
|
||||||
|
|
||||||
|
metaphor: |
|
||||||
|
Imagine being dropped into a maze viewed from above. You can move up, down,
|
||||||
|
left, or right, but walls block certain paths. DFS is like exploring one
|
||||||
|
corridor completely before backtracking. BFS is like sending scouts in all
|
||||||
|
directions simultaneously—whoever reaches the exit first found the shortest path.
|
||||||
|
|
||||||
|
Another analogy: painting a floor. Flood fill (DFS/BFS) starts at one point
|
||||||
|
and spreads to all connected unpainted tiles.
|
||||||
|
|
||||||
|
core_concept: |
|
||||||
|
A 2D grid is essentially a graph where each cell connects to its neighbors
|
||||||
|
(4-directional or 8-directional). The key adaptations from graph traversal:
|
||||||
|
|
||||||
|
1. **Nodes are coordinates**: Instead of node IDs, track `(row, col)` pairs
|
||||||
|
2. **Edges are directions**: Neighbors are found by adding direction vectors
|
||||||
|
3. **Bounds checking**: Validate coordinates before accessing the grid
|
||||||
|
4. **In-place visited marking**: Often modify the grid itself instead of using
|
||||||
|
a separate visited set (mark '1' → '0' to indicate visited)
|
||||||
|
|
||||||
|
**When to use DFS vs BFS:**
|
||||||
|
- **DFS**: Counting regions, checking if path exists, flood fill
|
||||||
|
- **BFS**: Shortest path, simultaneous spreading from multiple sources
|
||||||
|
|
||||||
|
visualization: |
|
||||||
|
**Example: Number of Islands**
|
||||||
|
|
||||||
|
```
|
||||||
|
Grid (1 = land, 0 = water):
|
||||||
|
|
||||||
|
1 1 0 0 0
|
||||||
|
1 1 0 0 0
|
||||||
|
0 0 1 0 0
|
||||||
|
0 0 0 1 1
|
||||||
|
|
||||||
|
DFS from (0,0): marks all connected 1s as visited
|
||||||
|
→ Found island 1
|
||||||
|
|
||||||
|
Continue scanning, DFS from (2,2):
|
||||||
|
→ Found island 2
|
||||||
|
|
||||||
|
Continue scanning, DFS from (3,3):
|
||||||
|
→ Found island 3
|
||||||
|
|
||||||
|
Answer: 3 islands
|
||||||
|
```
|
||||||
|
|
||||||
|
**Shortest path in grid (BFS):**
|
||||||
|
|
||||||
|
```
|
||||||
|
Grid (0 = passable, 1 = wall):
|
||||||
|
|
||||||
|
0 0 0
|
||||||
|
1 1 0
|
||||||
|
0 0 0
|
||||||
|
|
||||||
|
Start: (0,0) End: (2,2)
|
||||||
|
|
||||||
|
BFS levels:
|
||||||
|
Level 0: (0,0)
|
||||||
|
Level 1: (0,1)
|
||||||
|
Level 2: (0,2)
|
||||||
|
Level 3: (1,2)
|
||||||
|
Level 4: (2,2) ← Found! Distance = 4
|
||||||
|
```
|
||||||
|
|
||||||
|
**4-directional vs 8-directional:**
|
||||||
|
|
||||||
|
```
|
||||||
|
4-directional: 8-directional:
|
||||||
|
↑ ↖ ↑ ↗
|
||||||
|
← ○ → ← ○ →
|
||||||
|
↓ ↙ ↓ ↘
|
||||||
|
```
|
||||||
|
|
||||||
|
code_template: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
# Direction vectors
|
||||||
|
DIRS_4 = [(0, 1), (0, -1), (1, 0), (-1, 0)] # right, left, down, up
|
||||||
|
DIRS_8 = [(0, 1), (0, -1), (1, 0), (-1, 0),
|
||||||
|
(1, 1), (1, -1), (-1, 1), (-1, -1)]
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid(grid: list[list], row: int, col: int) -> bool:
|
||||||
|
"""Check if coordinates are within grid bounds."""
|
||||||
|
return 0 <= row < len(grid) and 0 <= col < len(grid[0])
|
||||||
|
|
||||||
|
|
||||||
|
def dfs_matrix(grid: list[list[int]], row: int, col: int) -> int:
|
||||||
|
"""DFS to explore connected region, returns size."""
|
||||||
|
if not is_valid(grid, row, col) or grid[row][col] == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
grid[row][col] = 0 # Mark as visited (modify in-place)
|
||||||
|
size = 1
|
||||||
|
|
||||||
|
for dr, dc in DIRS_4:
|
||||||
|
size += dfs_matrix(grid, row + dr, col + dc)
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
|
def count_islands(grid: list[list[str]]) -> int:
|
||||||
|
"""Count connected regions of '1's."""
|
||||||
|
if not grid:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if grid[r][c] == '1':
|
||||||
|
count += 1
|
||||||
|
dfs_sink_island(grid, r, c)
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def dfs_sink_island(grid: list[list[str]], row: int, col: int):
|
||||||
|
"""Sink an island by marking all connected '1's as '0'."""
|
||||||
|
if not is_valid(grid, row, col) or grid[row][col] == '0':
|
||||||
|
return
|
||||||
|
|
||||||
|
grid[row][col] = '0' # Sink this cell
|
||||||
|
|
||||||
|
for dr, dc in DIRS_4:
|
||||||
|
dfs_sink_island(grid, row + dr, col + dc)
|
||||||
|
|
||||||
|
|
||||||
|
def bfs_shortest_path(grid: list[list[int]],
|
||||||
|
start: tuple, end: tuple) -> int:
|
||||||
|
"""BFS to find shortest path in grid (0 = passable, 1 = wall)."""
|
||||||
|
if grid[start[0]][start[1]] == 1 or grid[end[0]][end[1]] == 1:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
queue = deque([(*start, 0)]) # (row, col, distance)
|
||||||
|
visited = {start}
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
row, col, dist = queue.popleft()
|
||||||
|
|
||||||
|
if (row, col) == end:
|
||||||
|
return dist
|
||||||
|
|
||||||
|
for dr, dc in DIRS_4:
|
||||||
|
nr, nc = row + dr, col + dc
|
||||||
|
if (is_valid(grid, nr, nc)
|
||||||
|
and (nr, nc) not in visited
|
||||||
|
and grid[nr][nc] == 0):
|
||||||
|
visited.add((nr, nc))
|
||||||
|
queue.append((nr, nc, dist + 1))
|
||||||
|
|
||||||
|
return -1 # No path
|
||||||
|
|
||||||
|
|
||||||
|
def multi_source_bfs(grid: list[list[int]]) -> int:
|
||||||
|
"""BFS from multiple sources (e.g., rotting oranges)."""
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
queue = deque()
|
||||||
|
fresh = 0
|
||||||
|
|
||||||
|
# Find all sources and count targets
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if grid[r][c] == 2: # Rotten orange
|
||||||
|
queue.append((r, c, 0))
|
||||||
|
elif grid[r][c] == 1: # Fresh orange
|
||||||
|
fresh += 1
|
||||||
|
|
||||||
|
max_time = 0
|
||||||
|
while queue:
|
||||||
|
row, col, time = queue.popleft()
|
||||||
|
max_time = max(max_time, time)
|
||||||
|
|
||||||
|
for dr, dc in DIRS_4:
|
||||||
|
nr, nc = row + dr, col + dc
|
||||||
|
if is_valid(grid, nr, nc) and grid[nr][nc] == 1:
|
||||||
|
grid[nr][nc] = 2 # Mark as rotten
|
||||||
|
fresh -= 1
|
||||||
|
queue.append((nr, nc, time + 1))
|
||||||
|
|
||||||
|
return max_time if fresh == 0 else -1
|
||||||
|
|
||||||
|
recognition_signals:
|
||||||
|
- "grid"
|
||||||
|
- "matrix"
|
||||||
|
- "2D array"
|
||||||
|
- "number of islands"
|
||||||
|
- "shortest path in grid"
|
||||||
|
- "flood fill"
|
||||||
|
- "surrounded regions"
|
||||||
|
- "rotting oranges"
|
||||||
|
- "word search"
|
||||||
|
- "robot path"
|
||||||
|
- "maze"
|
||||||
|
|
||||||
|
common_mistakes:
|
||||||
|
- title: Modifying grid while using it for bounds checking
|
||||||
|
description: |
|
||||||
|
If you use grid values for validity checks (like `grid[r][c] == '1'`)
|
||||||
|
and also modify them in-place, you might check a cell you already modified.
|
||||||
|
fix: |
|
||||||
|
Modify the cell immediately upon visiting, before exploring neighbors:
|
||||||
|
```python
|
||||||
|
grid[row][col] = '0' # Mark first
|
||||||
|
for dr, dc in DIRS_4:
|
||||||
|
dfs(grid, row + dr, col + dc) # Then explore
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Forgetting to check all starting points
|
||||||
|
description: |
|
||||||
|
For problems like "number of islands," only starting DFS from (0,0) misses
|
||||||
|
islands not connected to the top-left.
|
||||||
|
fix: |
|
||||||
|
Iterate through every cell and start a new traversal from each unvisited cell:
|
||||||
|
```python
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if grid[r][c] == '1':
|
||||||
|
count += 1
|
||||||
|
dfs(grid, r, c)
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Index out of bounds
|
||||||
|
description: |
|
||||||
|
Accessing `grid[row][col]` without checking bounds first causes runtime
|
||||||
|
errors when exploring neighbors.
|
||||||
|
fix: |
|
||||||
|
Always check bounds before accessing:
|
||||||
|
```python
|
||||||
|
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == target:
|
||||||
|
# Safe to access
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Using wrong direction set
|
||||||
|
description: |
|
||||||
|
Using 4-directional movement when problem requires 8-directional (diagonal)
|
||||||
|
gives wrong results, or vice versa.
|
||||||
|
fix: |
|
||||||
|
Read the problem carefully. "Adjacent" usually means 4-directional.
|
||||||
|
"Neighboring" or explicit diagonal mention means 8-directional.
|
||||||
|
|
||||||
|
variations:
|
||||||
|
- name: Island counting
|
||||||
|
description: |
|
||||||
|
Count connected regions of the same value. DFS from each unvisited cell,
|
||||||
|
marking the entire region as visited.
|
||||||
|
example: "Number of Islands, Max Area of Island"
|
||||||
|
|
||||||
|
- name: Shortest path in grid
|
||||||
|
description: |
|
||||||
|
BFS from start to end, counting levels. Works only if all moves have
|
||||||
|
equal cost (unweighted).
|
||||||
|
example: "Shortest Path in Binary Matrix, 01 Matrix"
|
||||||
|
|
||||||
|
- name: Multi-source BFS
|
||||||
|
description: |
|
||||||
|
Start BFS from multiple cells simultaneously. Useful for spreading
|
||||||
|
problems where multiple sources expand in parallel.
|
||||||
|
example: "Rotting Oranges, Walls and Gates"
|
||||||
|
|
||||||
|
- name: Word search
|
||||||
|
description: |
|
||||||
|
DFS with backtracking to find if a word exists in the grid by following
|
||||||
|
adjacent cells. Need to track the current path to avoid reusing cells.
|
||||||
|
example: "Word Search, Word Search II"
|
||||||
|
|
||||||
|
- name: Spiral traversal
|
||||||
|
description: |
|
||||||
|
Not graph-based, but iterate through matrix in spiral order using
|
||||||
|
boundary tracking (top, bottom, left, right).
|
||||||
|
example: "Spiral Matrix, Spiral Matrix II"
|
||||||
|
|
||||||
|
related_patterns:
|
||||||
|
- dfs
|
||||||
|
- bfs
|
||||||
|
- backtracking
|
||||||
|
|
||||||
|
prerequisite_patterns:
|
||||||
|
- dfs
|
||||||
|
- bfs
|
||||||
299
backend/data/patterns/tree-traversal.yaml
Normal file
299
backend/data/patterns/tree-traversal.yaml
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
name: Binary Tree Traversal
|
||||||
|
slug: tree-traversal
|
||||||
|
difficulty_level: 2
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Visit all nodes in a binary tree in specific orders: preorder (root-left-right),
|
||||||
|
inorder (left-root-right), postorder (left-right-root), or level-order (BFS).
|
||||||
|
Each order reveals different structural information about the tree.
|
||||||
|
|
||||||
|
when_to_use: |
|
||||||
|
- Serializing/deserializing trees
|
||||||
|
- Validating BST properties (inorder gives sorted order)
|
||||||
|
- Computing tree properties (height, size, sum)
|
||||||
|
- Copying or comparing trees
|
||||||
|
- Path sum and path finding problems
|
||||||
|
|
||||||
|
metaphor: |
|
||||||
|
Imagine reading a family tree. **Preorder** is like announcing yourself first,
|
||||||
|
then introducing your children. **Inorder** is alphabetical—left child, then
|
||||||
|
you, then right child. **Postorder** is like calculating taxes—you need to
|
||||||
|
know your children's totals before computing your own.
|
||||||
|
|
||||||
|
Another way to think about it: **preorder** is top-down (decisions flow from
|
||||||
|
root to leaves), **postorder** is bottom-up (results bubble from leaves to root).
|
||||||
|
|
||||||
|
core_concept: |
|
||||||
|
The three traversal orders differ only in *when* you process the current node
|
||||||
|
relative to its children:
|
||||||
|
|
||||||
|
- **Preorder**: Process **before** children → good for copying trees, prefix expressions
|
||||||
|
- **Inorder**: Process **between** children → BST gives sorted order
|
||||||
|
- **Postorder**: Process **after** children → good for deletion, evaluating expressions
|
||||||
|
|
||||||
|
The key insight is that these traversals naturally map to different problems:
|
||||||
|
|
||||||
|
- Need to see parents before children? → **Preorder**
|
||||||
|
- Need to aggregate child results? → **Postorder**
|
||||||
|
- Need sorted BST elements? → **Inorder**
|
||||||
|
- Need level-by-level? → **BFS**
|
||||||
|
|
||||||
|
visualization: |
|
||||||
|
**Example tree:**
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
/ \
|
||||||
|
2 3
|
||||||
|
/ \
|
||||||
|
4 5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Preorder (Root → Left → Right):**
|
||||||
|
```
|
||||||
|
Visit 1 → Visit 2 → Visit 4 → Visit 5 → Visit 3
|
||||||
|
Result: [1, 2, 4, 5, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inorder (Left → Root → Right):**
|
||||||
|
```
|
||||||
|
Visit 4 → Visit 2 → Visit 5 → Visit 1 → Visit 3
|
||||||
|
Result: [4, 2, 5, 1, 3]
|
||||||
|
|
||||||
|
For BST: gives elements in sorted order!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Postorder (Left → Right → Root):**
|
||||||
|
```
|
||||||
|
Visit 4 → Visit 5 → Visit 2 → Visit 3 → Visit 1
|
||||||
|
Result: [4, 5, 2, 3, 1]
|
||||||
|
|
||||||
|
Children processed before parent—useful for computing heights/sizes.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level-order (BFS):**
|
||||||
|
```
|
||||||
|
Level 0: [1]
|
||||||
|
Level 1: [2, 3]
|
||||||
|
Level 2: [4, 5]
|
||||||
|
Result: [1, 2, 3, 4, 5]
|
||||||
|
```
|
||||||
|
|
||||||
|
code_template: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
class TreeNode:
|
||||||
|
def __init__(self, val=0, left=None, right=None):
|
||||||
|
self.val = val
|
||||||
|
self.left = left
|
||||||
|
self.right = right
|
||||||
|
|
||||||
|
|
||||||
|
def preorder_recursive(root: TreeNode) -> list:
|
||||||
|
"""Preorder: Root → Left → Right"""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
return ([root.val]
|
||||||
|
+ preorder_recursive(root.left)
|
||||||
|
+ preorder_recursive(root.right))
|
||||||
|
|
||||||
|
|
||||||
|
def preorder_iterative(root: TreeNode) -> list:
|
||||||
|
"""Iterative preorder using stack."""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
stack = [root]
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
result.append(node.val)
|
||||||
|
# Push right first so left is processed first
|
||||||
|
if node.right:
|
||||||
|
stack.append(node.right)
|
||||||
|
if node.left:
|
||||||
|
stack.append(node.left)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def inorder_recursive(root: TreeNode) -> list:
|
||||||
|
"""Inorder: Left → Root → Right"""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
return (inorder_recursive(root.left)
|
||||||
|
+ [root.val]
|
||||||
|
+ inorder_recursive(root.right))
|
||||||
|
|
||||||
|
|
||||||
|
def inorder_iterative(root: TreeNode) -> list:
|
||||||
|
"""Iterative inorder using stack."""
|
||||||
|
result = []
|
||||||
|
stack = []
|
||||||
|
current = root
|
||||||
|
|
||||||
|
while current or stack:
|
||||||
|
# Go left as far as possible
|
||||||
|
while current:
|
||||||
|
stack.append(current)
|
||||||
|
current = current.left
|
||||||
|
|
||||||
|
# Process current node
|
||||||
|
current = stack.pop()
|
||||||
|
result.append(current.val)
|
||||||
|
|
||||||
|
# Move to right subtree
|
||||||
|
current = current.right
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def postorder_recursive(root: TreeNode) -> list:
|
||||||
|
"""Postorder: Left → Right → Root"""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
return (postorder_recursive(root.left)
|
||||||
|
+ postorder_recursive(root.right)
|
||||||
|
+ [root.val])
|
||||||
|
|
||||||
|
|
||||||
|
def postorder_iterative(root: TreeNode) -> list:
|
||||||
|
"""Iterative postorder using two stacks."""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
stack = [root]
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
result.append(node.val)
|
||||||
|
if node.left:
|
||||||
|
stack.append(node.left)
|
||||||
|
if node.right:
|
||||||
|
stack.append(node.right)
|
||||||
|
|
||||||
|
return result[::-1] # Reverse for postorder
|
||||||
|
|
||||||
|
|
||||||
|
def level_order(root: TreeNode) -> list[list]:
|
||||||
|
"""Level-order traversal using BFS."""
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
queue = deque([root])
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
level = []
|
||||||
|
for _ in range(len(queue)):
|
||||||
|
node = queue.popleft()
|
||||||
|
level.append(node.val)
|
||||||
|
if node.left:
|
||||||
|
queue.append(node.left)
|
||||||
|
if node.right:
|
||||||
|
queue.append(node.right)
|
||||||
|
result.append(level)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
recognition_signals:
|
||||||
|
- "binary tree"
|
||||||
|
- "tree traversal"
|
||||||
|
- "preorder"
|
||||||
|
- "inorder"
|
||||||
|
- "postorder"
|
||||||
|
- "level order"
|
||||||
|
- "serialize tree"
|
||||||
|
- "flatten tree"
|
||||||
|
- "BST to sorted"
|
||||||
|
- "kth smallest in BST"
|
||||||
|
- "validate BST"
|
||||||
|
- "path sum"
|
||||||
|
|
||||||
|
common_mistakes:
|
||||||
|
- title: Stack order in iterative preorder
|
||||||
|
description: |
|
||||||
|
Pushing left child before right child processes right first because
|
||||||
|
stacks are LIFO.
|
||||||
|
fix: |
|
||||||
|
Push right child first, then left child:
|
||||||
|
```python
|
||||||
|
if node.right:
|
||||||
|
stack.append(node.right) # Push right first
|
||||||
|
if node.left:
|
||||||
|
stack.append(node.left) # Left popped first
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Iterative inorder is tricky
|
||||||
|
description: |
|
||||||
|
The iterative inorder traversal is harder to get right than preorder or
|
||||||
|
postorder because you need to track when to go left vs when to process.
|
||||||
|
fix: |
|
||||||
|
Use the "go left until null, then process and go right" pattern:
|
||||||
|
```python
|
||||||
|
while current or stack:
|
||||||
|
while current:
|
||||||
|
stack.append(current)
|
||||||
|
current = current.left
|
||||||
|
current = stack.pop()
|
||||||
|
process(current)
|
||||||
|
current = current.right
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Forgetting null checks
|
||||||
|
description: |
|
||||||
|
Accessing `node.left` or `node.right` without checking if node is None
|
||||||
|
causes attribute errors.
|
||||||
|
fix: |
|
||||||
|
Always check for null nodes first:
|
||||||
|
```python
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
```
|
||||||
|
|
||||||
|
- title: Modifying tree during traversal
|
||||||
|
description: |
|
||||||
|
Changing node values or structure while traversing can cause missed nodes
|
||||||
|
or infinite loops.
|
||||||
|
fix: |
|
||||||
|
Collect nodes to modify in a separate pass, or use a traversal that
|
||||||
|
processes children before modifying the parent.
|
||||||
|
|
||||||
|
variations:
|
||||||
|
- name: Morris Traversal
|
||||||
|
description: |
|
||||||
|
Inorder traversal using O(1) space by temporarily modifying the tree
|
||||||
|
(threading right pointers to inorder successors).
|
||||||
|
example: "Recover Binary Search Tree, Inorder without stack"
|
||||||
|
|
||||||
|
- name: Boundary Traversal
|
||||||
|
description: |
|
||||||
|
Traverse only the boundary of the tree: left boundary, leaves, right
|
||||||
|
boundary in reverse.
|
||||||
|
example: "Boundary of Binary Tree"
|
||||||
|
|
||||||
|
- name: Vertical Order Traversal
|
||||||
|
description: |
|
||||||
|
Group nodes by their horizontal distance from root. Nodes at same
|
||||||
|
horizontal distance are in the same vertical line.
|
||||||
|
example: "Vertical Order Traversal, Binary Tree Right Side View"
|
||||||
|
|
||||||
|
- name: Zigzag Level Order
|
||||||
|
description: |
|
||||||
|
Level-order but alternating direction: left-to-right, then right-to-left,
|
||||||
|
and so on.
|
||||||
|
example: "Binary Tree Zigzag Level Order Traversal"
|
||||||
|
|
||||||
|
- name: Tree Serialization
|
||||||
|
description: |
|
||||||
|
Use traversal order to convert tree to string and back. Preorder with
|
||||||
|
null markers is common.
|
||||||
|
example: "Serialize and Deserialize Binary Tree"
|
||||||
|
|
||||||
|
related_patterns:
|
||||||
|
- dfs
|
||||||
|
- bfs
|
||||||
|
|
||||||
|
prerequisite_patterns: []
|
||||||
Reference in New Issue
Block a user