UNPKG

graph-builder

Version:

A graph builder library for modeling abstract graph structures.

232 lines (231 loc) 8.87 kB
import { SuccessorsFunction, SuccessorsAccessor } from "./SuccessorsFunction"; /** * Create a traverser for traversing a graph or tree. * * @remarks * * There are two entry points for creating a `Traverser`: {@link * Traversers.forTree} and {@link Traversers.forGraph}. You should choose one * based on your answers to the following question: * * Is there only one path to any node that's reachable from any start node? (If so, the * graph to be traversed is a tree or forest even if it is a subgraph of a graph which is * neither.) * * If your answer is: * * - (1) "no" use {@link Traversers.forGraph}. * * - (1) "yes" use {@link Traversers.forTree}. * * @public */ export declare class Traversers { /** * Creates a new {@link Traverser} for the given general `graph`. * * @remarks * * Traversers created using this method are guaranteed to visit each node reachable from the * start node(s) at most once. * * If you know that no node in `graph` is reachable by more than one path from the start * node(s), consider using {@link Traversers.forTree} instead. * * <b>Performance notes</b> * * <ul> * <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from * the start node). * <li>While traversing, the traverser will use <i>O(n)</i> space (where <i>n</i> is the number * of nodes that have thus far been visited), plus <i>O(H)</i> space (where <i>H</i> is the * number of nodes that have been seen but not yet visited, that is, the "horizon"). * </ul> */ static forGraph<N>(graph: SuccessorsAccessor<N>): Traverser<N>; static forGraph<N>(graph: SuccessorsFunction<N>): Traverser<N>; /** * Creates a new {@link Traverser} for a directed acyclic graph that has at most one path from the start * node(s) to any node reachable from the start node(s), and has no paths from any start node to * any other start node, such as a tree or forest. * * @remarks * * `forTree()` is especially useful (versus `forGraph()`) in cases where the data * structure being traversed is, in addition to being a tree/forest, also defined <a * href="https://github.com/google/guava/wiki/GraphsExplained#non-recursiveness">recursively</a>. * This is because the `forTree()`-based implementations don't keep track of visited nodes, * and therefore don't need to compare node objects; this saves * both time and space versus traversing the same graph using `forGraph()`. * * Providing a graph to be traversed for which there is more than one path from the start * node(s) to any node may lead to: * * <ul> * <li>Traversal not terminating (if the graph has cycles) * <li>Nodes being visited multiple times (if multiple paths exist from any start node to any * node reachable from any start node) * </ul> * * <b>Performance notes</b> * * <ul> * <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from * the start node). * <li>While traversing, the traverser will use <i>O(H)</i> space (where <i>H</i> is the number * of nodes that have been seen but not yet visited, that is, the "horizon"). * </ul> * * <b>Examples</b> (all edges are directed facing downwards) * * The graph below would be valid input with start nodes of `a, f, c`. However, if * `b` were <i>also</i> a start node, then there would be multiple paths to reach `e` and * `h`. * * ``` * a b c * / \ / \ | * / \ / \ | * d e f g * | * | * h * ``` * * . * * The graph below would be a valid input with start nodes of `a, f`. However, if * `b` were a start node, there would be multiple paths to `f`. * * ``` * a b * / \ / \ * / \ / \ * c d e * \ / * \ / * f * ``` * * <b>Note on binary trees</b> * * This method can be used to traverse over a binary tree. Given methods * `leftChild(node)` and `rightChild(node)`, this method can be called as * * ``` * Traversers.forTree(node => { successors: new Set([leftChild(node), rightChild(node)] }); * ``` */ static forTree<N>(tree: SuccessorsAccessor<N>): Traverser<N>; static forTree<N>(tree: SuccessorsFunction<N>): Traverser<N>; } /** * An object that can traverse the nodes that are reachable from a specified (set of) start node(s) * using a specified {@link SuccessorsFunction}. * * @public */ export interface Traverser<N> { /** * Returns an unmodifiable `Iterable` over the nodes reachable from `startNode`, in * the order of a breadth-first traversal. That is, all the nodes of depth 0 are returned, then * depth 1, then 2, and so on. * * @remarks * * <b>Example:</b> The following graph with `startNode` `a` would return nodes in * the order `abcdef` (assuming successors are returned in alphabetical order). * * ``` * b ---- a ---- d * | | * | | * e ---- c ---- f * ``` * * The behavior of this method is undefined if the nodes, or the topology of the graph, change * while iteration is in progress. * * The returned `Iterable` can be iterated over multiple times. Every iterator will * compute its next element on the fly. It is thus possible to limit the traversal to a certain * number of nodes as follows: * * ``` * Iterables.limit(Traversers.forGraph(graph).breadthFirst(node), maxNumberOfNodes); * ``` * * See <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Wikipedia</a> for more * info. * * Throws IllegalArgumentException if `startNode` is not an element of the graph. */ breadthFirst(...startNodes: Array<N>): Iterable<N>; /** * Returns an unmodifiable `Iterable` over the nodes reachable from `startNode`, in * the order of a depth-first pre-order traversal. "Pre-order" implies that nodes appear in the * `Iterable` in the order in which they are first visited. * * @remarks * * <b>Example:</b> The following graph with `startNode` `a` would return nodes in * the order `abecfd` (assuming successors are returned in alphabetical order). * * ``` * b ---- a ---- d * | | * | | * e ---- c ---- f * ``` * * The behavior of this method is undefined if the nodes, or the topology of the graph, change * while iteration is in progress. * * The returned `Iterable` can be iterated over multiple times. Every iterator will * compute its next element on the fly. It is thus possible to limit the traversal to a certain * number of nodes as follows: * * ``` * Iterables.limit( * Traversers.forGraph(graph).depthFirstPreOrder(node), maxNumberOfNodes); * ``` * * See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info. * * Throws IllegalArgumentException if `startNode` is not an element of the graph. */ depthFirstPreOrder(...startNodes: Array<N>): Iterable<N>; /** * Returns an unmodifiable `Iterable` over the nodes reachable from `startNode`, in * the order of a depth-first post-order traversal. "Post-order" implies that nodes appear in the * `Iterable` in the order in which they are visited for the last time. * * @remarks * * <b>Example:</b> The following graph with `startNode` `a` would return nodes in * the order `fcebda` (assuming successors are returned in alphabetical order). * * ``` * b ---- a ---- d * | | * | | * e ---- c ---- f * ``` * * The behavior of this method is undefined if the nodes, or the topology of the graph, change * while iteration is in progress. * * The returned `Iterable` can be iterated over multiple times. Every iterator will * compute its next element on the fly. It is thus possible to limit the traversal to a certain * number of nodes as follows: * * ``` * Iterables.limit( * Traversers.forGraph(graph).depthFirstPostOrder(node), maxNumberOfNodes); * ``` * * See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info. * * Throws IllegalArgumentException if `startNode` is not an element of the graph. */ depthFirstPostOrder(...startNodes: Array<N>): Iterable<N>; }