dist-javascript-algorithms-and-data-structures
Version:
Algorithms and data-structures implemented on JavaScript
99 lines (80 loc) • 3.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = detectDirectedCycle;
var _depthFirstSearch = _interopRequireDefault(require("../depth-first-search/depthFirstSearch"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Detect cycle in directed graph using Depth First Search.
*
* @param {Graph} graph
*/
function detectDirectedCycle(graph) {
let cycle = null; // Will store parents (previous vertices) for all visited nodes.
// This will be needed in order to specify what path exactly is a cycle.
const dfsParentMap = {}; // White set (UNVISITED) contains all the vertices that haven't been visited at all.
const whiteSet = {}; // Gray set (VISITING) contains all the vertices that are being visited right now
// (in current path).
const graySet = {}; // Black set (VISITED) contains all the vertices that has been fully visited.
// Meaning that all children of the vertex has been visited.
const blackSet = {}; // If we encounter vertex in gray set it means that we've found a cycle.
// Because when vertex in gray set it means that its neighbors or its neighbors
// neighbors are still being explored.
// Init white set and add all vertices to it.
/** @param {GraphVertex} vertex */
graph.getAllVertices().forEach(vertex => {
whiteSet[vertex.getKey()] = vertex;
}); // Describe BFS callbacks.
const callbacks = {
enterVertex: ({
currentVertex,
previousVertex
}) => {
if (graySet[currentVertex.getKey()]) {
// If current vertex already in grey set it means that cycle is detected.
// Let's detect cycle path.
cycle = {};
let currentCycleVertex = currentVertex;
let previousCycleVertex = previousVertex;
while (previousCycleVertex.getKey() !== currentVertex.getKey()) {
cycle[currentCycleVertex.getKey()] = previousCycleVertex;
currentCycleVertex = previousCycleVertex;
previousCycleVertex = dfsParentMap[previousCycleVertex.getKey()];
}
cycle[currentCycleVertex.getKey()] = previousCycleVertex;
} else {
// Otherwise let's add current vertex to gray set and remove it from white set.
graySet[currentVertex.getKey()] = currentVertex;
delete whiteSet[currentVertex.getKey()]; // Update DFS parents list.
dfsParentMap[currentVertex.getKey()] = previousVertex;
}
},
leaveVertex: ({
currentVertex
}) => {
// If all node's children has been visited let's remove it from gray set
// and move it to the black set meaning that all its neighbors are visited.
blackSet[currentVertex.getKey()] = currentVertex;
delete graySet[currentVertex.getKey()];
},
allowTraversal: ({
nextVertex
}) => {
// If cycle was detected we must forbid all further traversing since it will
// cause infinite traversal loop.
if (cycle) {
return false;
} // Allow traversal only for the vertices that are not in black set
// since all black set vertices have been already visited.
return !blackSet[nextVertex.getKey()];
}
}; // Start exploring vertices.
while (Object.keys(whiteSet).length) {
// Pick fist vertex to start BFS from.
const firstWhiteKey = Object.keys(whiteSet)[0];
const startVertex = whiteSet[firstWhiteKey]; // Do Depth First Search.
(0, _depthFirstSearch.default)(graph, startVertex, callbacks);
}
return cycle;
}