mirror of
https://github.com/20kaushik02/spotify-manager.git
synced 2026-01-25 06:04:05 +00:00
MASSIVE commit
- moved to typescript - axios rate limitmodule is busted, removed for now, do something else for that - sequelize-typescript - dotenv, not dotenv-flow - removed playlist details route types for API ton of minor fixes and improvements
This commit is contained in:
138
utils/graph.ts
Normal file
138
utils/graph.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
export type GNode = string;
|
||||
export type GEdge = { from: string; to: string };
|
||||
|
||||
/**
|
||||
* Directed graph, may or may not be connected.
|
||||
*
|
||||
* NOTE: Assumes that nodes and edges are valid.
|
||||
*
|
||||
* Example:
|
||||
* ```javascript
|
||||
* let nodes = ["a", "b", "c", "d", "e"];
|
||||
* let edges = [
|
||||
* { from: "a", to: "b" },
|
||||
* { from: "b", to: "c" },
|
||||
* { from: "c", to: "d" },
|
||||
* { from: "d", to: "a" },
|
||||
* { from: "e", to: "a" }
|
||||
* ];
|
||||
* let g = new myGraph(nodes, edges);
|
||||
* console.log(g.detectCycle()); // true
|
||||
* ```
|
||||
*/
|
||||
export class myGraph {
|
||||
nodes: GNode[];
|
||||
edges: GEdge[];
|
||||
/**
|
||||
* @param nodes Graph nodes IDs
|
||||
* @param edges Graph edges b/w nodes
|
||||
*/
|
||||
constructor(nodes: GNode[], edges: GEdge[]) {
|
||||
this.nodes = structuredClone(nodes);
|
||||
this.edges = structuredClone(edges);
|
||||
}
|
||||
|
||||
getDirectHeads(node: GNode): GNode[] {
|
||||
return this.edges
|
||||
.filter((edge) => edge.to == node)
|
||||
.map((edge) => edge.from);
|
||||
}
|
||||
|
||||
getDirectHeadEdges(node: GNode): GEdge[] {
|
||||
return this.edges.filter((edge) => edge.to == node);
|
||||
}
|
||||
|
||||
/** BFS */
|
||||
getAllHeads(node: GNode): GNode[] {
|
||||
const headSet = new Set<GNode>();
|
||||
const toVisit = new Set<GNode>(); // queue
|
||||
toVisit.add(node);
|
||||
while (toVisit.size > 0) {
|
||||
const nextNode = toVisit.values().next().value!;
|
||||
const nextHeads = this.getDirectHeads(nextNode);
|
||||
nextHeads.forEach((head) => {
|
||||
headSet.add(head);
|
||||
toVisit.add(head);
|
||||
});
|
||||
toVisit.delete(nextNode);
|
||||
}
|
||||
return [...headSet];
|
||||
}
|
||||
|
||||
getDirectTails(node: GNode): GNode[] {
|
||||
return this.edges
|
||||
.filter((edge) => edge.from == node)
|
||||
.map((edge) => edge.to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} node
|
||||
* @returns {{ from: string, to: string }[]}
|
||||
*/
|
||||
getDirectTailEdges(node: GNode): GEdge[] {
|
||||
return this.edges.filter((edge) => edge.from == node);
|
||||
}
|
||||
|
||||
/** BFS */
|
||||
getAllTails(node: GNode): GNode[] {
|
||||
const tailSet = new Set<GNode>();
|
||||
const toVisit = new Set<GNode>(); // queue
|
||||
toVisit.add(node);
|
||||
while (toVisit.size > 0) {
|
||||
const nextNode = toVisit.values().next().value!;
|
||||
const nextTails = this.getDirectTails(nextNode);
|
||||
nextTails.forEach((tail) => {
|
||||
tailSet.add(tail);
|
||||
toVisit.add(tail);
|
||||
});
|
||||
toVisit.delete(nextNode);
|
||||
}
|
||||
return [...tailSet];
|
||||
}
|
||||
|
||||
/** Kahn's topological sort */
|
||||
topoSort(): GNode[] {
|
||||
let inDegree: Record<string, number> = {};
|
||||
let zeroInDegreeQueue: GNode[] = [];
|
||||
let topologicalOrder: GNode[] = [];
|
||||
|
||||
// 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 tail of this.getDirectTails(node!)) {
|
||||
inDegree[tail]!--;
|
||||
if (inDegree[tail] === 0) {
|
||||
zeroInDegreeQueue.push(tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
return topologicalOrder;
|
||||
}
|
||||
|
||||
/** Check if the graph contains a cycle */
|
||||
detectCycle(): boolean {
|
||||
// If topological order includes all nodes, no cycle exists
|
||||
return this.topoSort().length < this.nodes.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default myGraph;
|
||||
Reference in New Issue
Block a user