Lesson 5 of 9 · 20m
Topological Sort and Dependency Order
Build systems, task schedulers, and course prerequisites all reduce to the same problem — order the nodes so every dependency comes first.
Build A before B because B depends on A" is a dependency graph. Topological sort produces the execution order — the reason Makefiles, CI pipelines, and course planners work.
Kahn's algorithm (BFS-based)
def topo_sort(graph):
in_degree = {n: 0 for n in graph}
for n in graph:
for m in graph[n]:
in_degree[m] += 1
queue = deque([n for n, d in in_degree.items() if d == 0])
order = []
while queue:
node = queue.popleft()
order.append(node)
for m in graph[node]:
in_degree[m] -= 1
if in_degree[m] == 0:
queue.append(m)
return order if len(order) == len(graph) else None # cycle → impossibleNodes with no dependencies go first; removing them reduces their dependents' in-degree. If the order is shorter than the graph, a cycle exists — "A depends on B, B depends on A" — which is a deadlock in build systems and circular-import bugs in code.
DFS-based topological sort
def dfs_topo(node, visited, order):
if node in visited: return
visited.add(node)
for m in graph[node]:
dfs_topo(m, visited, order)
order.append(node) # append *after* children — deepest firstSame result, different mechanics — the stack pops a node only after all its descendants are in the order.
::exercise(cycle-detector)
Field notes
graphlib.TopologicalSorterin Python stdlib,kahnortarjanin every language's graph lib — don't hand-roll unless learning.- A topological sort isn't unique — multiple valid orders exist (any valid order works; sometimes you want the lexicographically smallest, which needs a priority queue).
- The cycle-detection property is the feature:
"can't topologically sort" == "circular dependency".