Kahn’s Algorithm is an algorithm that finds a topological sort for a directed acyclic graph in time and space.
Algorithm 0
Lemma
Let denote the in-degree of , then
Proof
If , let be a vertex such that there exists an edge directed from to .
Let be a sequence of vertices in such that
Then applying the Pigeonhole Principle yields
Therefore, is a cycle in .
By contradiction, it follows that .
-
Let be a vertex in .
-
Find a topological sort of recursively.
-
is a topological sort of .
This algorithm solves the problem in time and space.
std::vector<int> kahn(int n, int m, const std::vector<int> &u, const std::vector<int> &v) {
std::vector<std::vector<int>> adj(n);
std::vector deg(n, 0);
for (int i = 0; i < m; i++) {
adj[u[i]].push_back(v[i]);
deg[v[i]]++;
}
std::vector<int> o;
for (auto s = std::ranges::to<std::vector>(std::views::iota(0, n)); !s.empty(); ) {
int u = std::ranges::min(s, {}, [&](int u) -> int {
return deg[u];
});
std::erase(s, u);
o.push_back(u);
for (int v : adj[u]) {
deg[v]--;
}
}
return o;
}Algorithm 1
Based on Algorithm 0, maintaining yields an algorithm that solves the problem in time and space.
std::vector<int> kahn(int n, int m, const std::vector<int> &u, const std::vector<int> &v) {
std::vector<std::vector<int>> adj(n);
std::vector deg(n, 0);
for (int i = 0; i < m; i++) {
adj[u[i]].push_back(v[i]);
deg[v[i]]++;
}
std::vector<int> o;
for (auto q = std::ranges::to<std::queue>(std::views::iota(0, n) | std::views::filter([&](int i) -> bool {
return !deg[i];
})); !q.empty(); ) {
int u = q.front();
q.pop();
o.push_back(u);
for (int v : adj[u]) {
if (!--deg[v]) {
q.push(v);
}
}
}
return o;
}