dist-javascript-algorithms-and-data-structures
Version:
Algorithms and data-structures implemented on JavaScript
142 lines (117 loc) • 5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = stronglyConnectedComponents;
var _Stack = _interopRequireDefault(require("../../../data-structures/stack/Stack"));
var _depthFirstSearch = _interopRequireDefault(require("../depth-first-search/depthFirstSearch"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @param {Graph} graph
* @return {Stack}
*/
function getVerticesSortedByDfsFinishTime(graph) {
// Set of all visited vertices during DFS pass.
const visitedVerticesSet = {}; // Stack of vertices by finish time.
// All vertices in this stack are ordered by finished time in decreasing order.
// Vertex that has been finished first will be at the bottom of the stack and
// vertex that has been finished last will be at the top of the stack.
const verticesByDfsFinishTime = new _Stack.default(); // Set of all vertices we're going to visit.
const notVisitedVerticesSet = {};
graph.getAllVertices().forEach(vertex => {
notVisitedVerticesSet[vertex.getKey()] = vertex;
}); // Specify DFS traversal callbacks.
const dfsCallbacks = {
enterVertex: ({
currentVertex
}) => {
// Add current vertex to visited set.
visitedVerticesSet[currentVertex.getKey()] = currentVertex; // Delete current vertex from not visited set.
delete notVisitedVerticesSet[currentVertex.getKey()];
},
leaveVertex: ({
currentVertex
}) => {
// Push vertex to the stack when leaving it.
// This will make stack to be ordered by finish time in decreasing order.
verticesByDfsFinishTime.push(currentVertex);
},
allowTraversal: ({
nextVertex
}) => {
// Don't allow to traverse the nodes that have been already visited.
return !visitedVerticesSet[nextVertex.getKey()];
}
}; // Do FIRST DFS PASS traversal for all graph vertices to fill the verticesByFinishTime stack.
while (Object.values(notVisitedVerticesSet).length) {
// Peek any vertex to start DFS traversal from.
const startVertexKey = Object.keys(notVisitedVerticesSet)[0];
const startVertex = notVisitedVerticesSet[startVertexKey];
delete notVisitedVerticesSet[startVertexKey];
(0, _depthFirstSearch.default)(graph, startVertex, dfsCallbacks);
}
return verticesByDfsFinishTime;
}
/**
* @param {Graph} graph
* @param {Stack} verticesByFinishTime
* @return {*[]}
*/
function getSCCSets(graph, verticesByFinishTime) {
// Array of arrays of strongly connected vertices.
const stronglyConnectedComponentsSets = []; // Array that will hold all vertices that are being visited during one DFS run.
let stronglyConnectedComponentsSet = []; // Visited vertices set.
const visitedVerticesSet = {}; // Callbacks for DFS traversal.
const dfsCallbacks = {
enterVertex: ({
currentVertex
}) => {
// Add current vertex to SCC set of current DFS round.
stronglyConnectedComponentsSet.push(currentVertex); // Add current vertex to visited set.
visitedVerticesSet[currentVertex.getKey()] = currentVertex;
},
leaveVertex: ({
previousVertex
}) => {
// Once DFS traversal is finished push the set of found strongly connected
// components during current DFS round to overall strongly connected components set.
// The sign that traversal is about to be finished is that we came back to start vertex
// which doesn't have parent.
if (previousVertex === null) {
stronglyConnectedComponentsSets.push([...stronglyConnectedComponentsSet]);
}
},
allowTraversal: ({
nextVertex
}) => {
// Don't allow traversal of already visited vertices.
return !visitedVerticesSet[nextVertex.getKey()];
}
};
while (!verticesByFinishTime.isEmpty()) {
/** @var {GraphVertex} startVertex */
const startVertex = verticesByFinishTime.pop(); // Reset the set of strongly connected vertices.
stronglyConnectedComponentsSet = []; // Don't do DFS on already visited vertices.
if (!visitedVerticesSet[startVertex.getKey()]) {
// Do DFS traversal.
(0, _depthFirstSearch.default)(graph, startVertex, dfsCallbacks);
}
}
return stronglyConnectedComponentsSets;
}
/**
* Kosaraju's algorithm.
*
* @param {Graph} graph
* @return {*[]}
*/
function stronglyConnectedComponents(graph) {
// In this algorithm we will need to do TWO DFS PASSES overt the graph.
// Get stack of vertices ordered by DFS finish time.
// All vertices in this stack are ordered by finished time in decreasing order:
// Vertex that has been finished first will be at the bottom of the stack and
// vertex that has been finished last will be at the top of the stack.
const verticesByFinishTime = getVerticesSortedByDfsFinishTime(graph); // Reverse the graph.
graph.reverse(); // Do DFS once again on reversed graph.
return getSCCSets(graph, verticesByFinishTime);
}