| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:collection'; |
| import 'dart:math' show min; |
| |
| /// Finds the strongly connected components of a directed graph using Tarjan's |
| /// algorithm. |
| /// |
| /// The result will be a valid reverse topological order ordering of the |
| /// strongly connected components. Components further from a root will appear in |
| /// the result before the components which they are connected to. |
| /// |
| /// Nodes within a strongly connected component have no ordering guarantees, |
| /// except that if the first value in [nodes] is a valid root, and is contained |
| /// in a cycle, it will be the last element of that cycle. |
| /// |
| /// [nodes] must contain at least a root of every tree in the graph if there are |
| /// disjoint subgraphs but it may contain all nodes in the graph if the roots |
| /// are not known. |
| /// |
| /// If [equals] is provided, it is used to compare nodes in the graph. If |
| /// [equals] is omitted, the node's own [Object.==] is used instead. |
| /// |
| /// Similarly, if [hashCode] is provided, it is used to produce a hash value |
| /// for nodes to efficiently calculate the return value. If it is omitted, the |
| /// key's own [Object.hashCode] is used. |
| /// |
| /// If you supply one of [equals] or [hashCode], you should generally also to |
| /// supply the other. |
| List<List<T>> stronglyConnectedComponents<T extends Object>( |
| Iterable<T> nodes, |
| Iterable<T> Function(T) edges, { |
| bool Function(T, T)? equals, |
| int Function(T)? hashCode, |
| }) { |
| final result = <List<T>>[]; |
| final lowLinks = HashMap<T, int>(equals: equals, hashCode: hashCode); |
| final indexes = HashMap<T, int>(equals: equals, hashCode: hashCode); |
| final onStack = HashSet<T>(equals: equals, hashCode: hashCode); |
| |
| final nonNullEquals = equals ?? _defaultEquals; |
| |
| var index = 0; |
| final lastVisited = Queue<T>(); |
| |
| final stack = [for (final node in nodes) _StackState(node)]; |
| outer: |
| while (stack.isNotEmpty) { |
| final state = stack.removeLast(); |
| final node = state.node; |
| var iterator = state.iterator; |
| |
| int lowLink; |
| if (iterator == null) { |
| if (indexes.containsKey(node)) continue; |
| indexes[node] = index; |
| lowLink = lowLinks[node] = index; |
| index++; |
| iterator = edges(node).iterator; |
| |
| // Nodes with no edges are always in their own component. |
| if (!iterator.moveNext()) { |
| result.add([node]); |
| continue; |
| } |
| |
| lastVisited.addLast(node); |
| onStack.add(node); |
| } else { |
| lowLink = min(lowLinks[node]!, lowLinks[iterator.current]!); |
| } |
| |
| do { |
| final next = iterator.current; |
| if (!indexes.containsKey(next)) { |
| stack.add(_StackState(node, iterator)); |
| stack.add(_StackState(next)); |
| continue outer; |
| } else if (onStack.contains(next)) { |
| lowLink = lowLinks[node] = min(lowLink, indexes[next]!); |
| } |
| } while (iterator.moveNext()); |
| |
| if (lowLink == indexes[node]) { |
| final component = <T>[]; |
| T next; |
| do { |
| next = lastVisited.removeLast(); |
| onStack.remove(next); |
| component.add(next); |
| } while (!nonNullEquals(next, node)); |
| result.add(component); |
| } |
| } |
| |
| return result; |
| } |
| |
| /// The state of a pass on a single node in Tarjan's Algorithm. |
| /// |
| /// This is used to perform the algorithm with an explicit stack rather than |
| /// recursively, to avoid stack overflow errors for very large graphs. |
| class _StackState<T> { |
| /// The node being inspected. |
| final T node; |
| |
| /// The iterator traversing [node]'s edges. |
| /// |
| /// This is null if the node hasn't yet begun being traversed. |
| final Iterator<T>? iterator; |
| |
| _StackState(this.node, [this.iterator]); |
| } |
| |
| bool _defaultEquals(Object a, Object b) => a == b; |