105 lines
3.1 KiB
YAML
105 lines
3.1 KiB
YAML
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
|