A DAG (directed acyclic graph) can be ordered so every edge goes forward. Kahn’s algorithm: queue nodes with indegree 0, peel them off. If items remain, there was a cycle.
Goal
Order kiosk opening tasks so dependencies come first.
Kahn
from collections import deque
edges = [
("buy_stock", "open_till"),
("unlock", "buy_stock"),
("unlock", "sweep"),
("sweep", "open_till"),
]
nodes = sorted({u for e in edges for u in e})
indeg = {n: 0 for n in nodes}
adj = {n: [] for n in nodes}
for a, b in edges:
adj[a].append(b)
indeg[b] += 1
q = deque([n for n in nodes if indeg[n] == 0])
order = []
while q:
n = q.popleft()
order.append(n)
for m in adj[n]:
indeg[m] -= 1
if indeg[m] == 0:
q.append(m)
print(order)
print("all", len(order) == len(nodes))unlock has indegree 0, so it starts. open_till waits until stock and sweep are done.
A cycle fails
from collections import deque
edges = [("A", "B"), ("B", "A")]
nodes = ["A", "B"]
indeg = {"A": 1, "B": 1}
adj = {"A": ["B"], "B": ["A"]}
q = deque([n for n in nodes if indeg[n] == 0])
order = []
while q:
n = q.popleft()
order.append(n)
for m in adj[n]:
indeg[m] -= 1
if indeg[m] == 0:
q.append(m)
print(order, "complete", len(order) == 2)Tip
Package installs, make, and course prerequisites are DAGs. If len(order) != len(nodes), refuse — the spec is cyclic.