feat(patterns): pattern taxonomy + is_optimal
This commit is contained in:
104
backend/data/patterns/topological-sort.yaml
Normal file
104
backend/data/patterns/topological-sort.yaml
Normal file
@@ -0,0 +1,104 @@
|
||||
name: Topological Sort
|
||||
slug: topological-sort
|
||||
difficulty_level: 3
|
||||
pattern_type: algorithm
|
||||
display_order: 22
|
||||
|
||||
description: >
|
||||
Order vertices in a directed acyclic graph (DAG) such that for every
|
||||
edge u -> v, vertex u comes before v in the ordering.
|
||||
|
||||
when_to_use: |
|
||||
- Course prerequisites / task dependencies
|
||||
- Build order / compilation order
|
||||
- Detecting cycles in directed graphs
|
||||
- Any problem with "do A before B" constraints
|
||||
|
||||
metaphor: |
|
||||
Imagine getting dressed: you must put on underwear before pants, socks
|
||||
before shoes. Topological sort finds a valid order that respects all
|
||||
"must come before" rules. If there's a cycle (shirt requires jacket,
|
||||
jacket requires shirt), no valid order exists.
|
||||
|
||||
core_concept: |
|
||||
Two main approaches:
|
||||
|
||||
**Kahn's Algorithm (BFS):** Start with nodes having no incoming edges
|
||||
(in-degree 0). Process them, remove their edges, repeat. If all nodes
|
||||
processed, valid order exists.
|
||||
|
||||
**DFS-based:** Do DFS, add nodes to result when backtracking (post-order).
|
||||
Reverse at end. Cycle exists if we revisit a node in current path.
|
||||
|
||||
code_template: |
|
||||
from collections import deque
|
||||
|
||||
def topological_sort_bfs(n: int, edges: list[tuple[int, int]]) -> list[int]:
|
||||
"""Kahn's algorithm - returns empty list if cycle exists."""
|
||||
# Build adjacency list and in-degree count
|
||||
graph = [[] for _ in range(n)]
|
||||
in_degree = [0] * n
|
||||
|
||||
for u, v in edges: # u -> v (u must come before v)
|
||||
graph[u].append(v)
|
||||
in_degree[v] += 1
|
||||
|
||||
# Start with nodes having no prerequisites
|
||||
queue = deque([i for i in range(n) if in_degree[i] == 0])
|
||||
result = []
|
||||
|
||||
while queue:
|
||||
node = queue.popleft()
|
||||
result.append(node)
|
||||
|
||||
for neighbor in graph[node]:
|
||||
in_degree[neighbor] -= 1
|
||||
if in_degree[neighbor] == 0:
|
||||
queue.append(neighbor)
|
||||
|
||||
# If we processed all nodes, valid topological order exists
|
||||
return result if len(result) == n else []
|
||||
|
||||
recognition_signals:
|
||||
- "prerequisites"
|
||||
- "dependencies"
|
||||
- "ordering tasks"
|
||||
- "course schedule"
|
||||
- "build order"
|
||||
- "detect cycle in directed graph"
|
||||
- "do X before Y"
|
||||
|
||||
common_mistakes:
|
||||
- title: Forgetting Cycle Detection
|
||||
description: |
|
||||
Assuming input is always a valid DAG. Must check if all nodes were
|
||||
processed (BFS) or if back-edge exists (DFS).
|
||||
fix: |
|
||||
BFS: Check `len(result) == n`. DFS: Track "visiting" state separately
|
||||
from "visited."
|
||||
|
||||
- title: Wrong Edge Direction
|
||||
description: |
|
||||
Confusing "A depends on B" vs "A must come before B." These are
|
||||
opposite edge directions.
|
||||
fix: |
|
||||
Clarify: If A depends on B, edge is B -> A (B comes before A).
|
||||
|
||||
variations:
|
||||
- name: Course Schedule (cycle detection)
|
||||
description: Return true/false if valid ordering exists
|
||||
example: "course-schedule"
|
||||
- name: Course Schedule II (find order)
|
||||
description: Return the actual ordering
|
||||
example: "course-schedule-ii"
|
||||
- name: Alien Dictionary
|
||||
description: Infer ordering from sorted alien words
|
||||
example: "alien-dictionary"
|
||||
|
||||
related_patterns:
|
||||
- bfs
|
||||
- dfs
|
||||
|
||||
prerequisite_patterns:
|
||||
- bfs
|
||||
- dfs
|
||||
Reference in New Issue
Block a user