dagre-d3-es
Version:
<p align="center"> <a href="https://tbo47.github.io/" ><img src="https://img.shields.io/badge/created_by-tbo47-blue.svg" alt="Created by tbo47"></a> <a href="https://www.npmjs.com/dagre-d3-es"><img src="https://img.shields.io/npm/v/dagre-d3-es.svg?log
101 lines (93 loc) • 2.66 kB
JavaScript
/**
* @import { Graph, NodeID } from '../graph.js';
*/
export { tarjan };
/**
* This function is an implementation of [Tarjan's algorithm][] which finds
* all [strongly connected components][] in the directed graph `g`. Each
* strongly connected component is composed of nodes that can reach all other
* nodes in the component via directed edges. A strongly connected component
* can consist of a single node if that node cannot both reach and be reached
* by any other specific node in the graph. Components of more than one node
* are guaranteed to have at least one cycle.
*
* [Tarjan's algorithm]: http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
* [strongly connected components]: http://en.wikipedia.org/wiki/Strongly_connected_component
*
* @example
*
* 
* <!-- SOURCE:
* digraph {
* node [shape=circle, style="fill:white;stroke:#333;stroke-width:1.5px"]
* edge [lineInterpolate=bundle]
* rankdir=lr
*
* A -> B -> C -> D -> H -> G -> F
* F -> G
* D -> C
* H -> D
* B -> E -> G
* E -> A
* }
* -->
*
* ```js
* graphlib.alg.tarjan(g);
* // => [ [ 'F', 'G' ],
* // [ 'H', 'D', 'C' ],
* // [ 'E', 'B', 'A' ] ]
* ```
*
* @param {Graph} g - The directed graph to analyze.
* @returns {NodeID[][]} an array of components. Each component is itself an
* array that contains the ids of all nodes in the component.
*/
function tarjan(g) {
var index = 0;
/** @type {NodeID[]} */
var stack = [];
/**
* @type {Record<NodeID, { onStack: boolean, lowlink: number, index: number }>}
*/
var visited = {};
/** @type {NodeID[][]} */
var results = [];
/**
* @param {NodeID} v - Node to recursively visit
*/
function dfs(v) {
var entry = (visited[v] = {
onStack: true,
lowlink: index,
index: index++,
});
stack.push(v);
g.successors(v).forEach(function (w) {
if (!Object.prototype.hasOwnProperty.call(visited, w)) {
dfs(w);
entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink);
} else if (visited[w].onStack) {
entry.lowlink = Math.min(entry.lowlink, visited[w].index);
}
});
if (entry.lowlink === entry.index) {
/** @type {NodeID[]} */
var cmpt = [];
/** @type {NodeID} */
var w;
do {
w = stack.pop();
visited[w].onStack = false;
cmpt.push(w);
} while (v !== w);
results.push(cmpt);
}
}
g.nodes().forEach(function (v) {
if (!Object.prototype.hasOwnProperty.call(visited, v)) {
dfs(v);
}
});
return results;
}