stronglyConnectedComponents< T> function
Returns the strongly connected components of graph
, in topological
order.
Interprets graph
as a directed graph with a vertex for each key and edges
from each key to the values that the key maps to.
Assumes that every vertex in the graph has a key to represent it, even if
that vertex has no outgoing edges. This isn't checked, but if it's not
satisfied, the function may crash or provide unexpected output. For example,
{"a": ["b"]}
is not valid, but {"a": ["b"], "b": []}
is.
Implementation
List<Set<T>> stronglyConnectedComponents<T>(Map<T, Iterable<T>> graph) {
// This uses [Tarjan's algorithm][].
//
// [Tarjan's algorithm]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
var index = 0;
var stack = <T>[];
var result = <Set<T>>[];
// The order of these doesn't matter, so we use un-linked implementations to
// avoid unnecessary overhead.
var indices = new HashMap<T, int>();
var lowLinks = new HashMap<T, int>();
var onStack = new HashSet<T>();
strongConnect(T vertex) {
indices[vertex] = index;
lowLinks[vertex] = index;
index++;
stack.add(vertex);
onStack.add(vertex);
for (var successor in graph[vertex]) {
if (!indices.containsKey(successor)) {
strongConnect(successor);
lowLinks[vertex] = math.min(lowLinks[vertex], lowLinks[successor]);
} else if (onStack.contains(successor)) {
lowLinks[vertex] = math.min(lowLinks[vertex], lowLinks[successor]);
}
}
if (lowLinks[vertex] == indices[vertex]) {
var component = new Set<T>();
T neighbor;
do {
neighbor = stack.removeLast();
onStack.remove(neighbor);
component.add(neighbor);
} while (neighbor != vertex);
result.add(component);
}
}
for (var vertex in graph.keys) {
if (!indices.containsKey(vertex)) strongConnect(vertex);
}
// Tarjan's algorithm produces a reverse-topological sort, so we reverse it to
// get a normal topological sort.
return result.reversed.toList();
}