graph-builder
Version:
A graph builder library for modeling abstract graph structures.
232 lines (231 loc) • 8.87 kB
TypeScript
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>;
}