const logger = require("./logger")(module); const typedefs = require("../typedefs"); /** * Directed graph, may or may not be connected. * * NOTE: Assumes that nodes and edges are valid. */ class myGraph { /** * @param {string[]} nodes Graph nodes IDs * @param {{ from: string, to: string }[]} edges Graph edges b/w nodes */ constructor(nodes, edges) { this.nodes = [...nodes]; this.edges = structuredClone(edges); } /** * @param {type} node * @returns {string[]} */ getNeighbors(node) { return this.edges.filter(edge => edge.from == node).map(edge => edge.to); } /** * Kahn's topological sort * @returns {string[]} */ topoSort() { let inDegree = {}; let zeroInDegreeQueue = []; let topologicalOrder = []; // Initialize inDegree of all nodes to 0 for (let node of this.nodes) { inDegree[node] = 0; } // Calculate inDegree of each node for (let edge of this.edges) { inDegree[edge.to]++; } // Collect nodes with 0 inDegree for (let node of this.nodes) { if (inDegree[node] === 0) { zeroInDegreeQueue.push(node); } } // process nodes with 0 inDegree while (zeroInDegreeQueue.length > 0) { let node = zeroInDegreeQueue.shift(); topologicalOrder.push(node); for (let neighbor of this.getNeighbors(node)) { inDegree[neighbor]--; if (inDegree[neighbor] === 0) { zeroInDegreeQueue.push(neighbor); } } } return topologicalOrder; } /** * Check if the graph contains a cycle * @returns {boolean} */ detectCycle() { // If topological order includes all nodes, no cycle exists return this.topoSort().length < this.nodes.length; } } module.exports = myGraph;