88 lines
2.1 KiB
JavaScript
88 lines
2.1 KiB
JavaScript
export default class Digraph {
|
|
|
|
arcs = new Map();
|
|
|
|
addDependency(head, tail) {
|
|
this.ensureTail(head);
|
|
this.ensureTail(tail).add(head);
|
|
}
|
|
|
|
detectCycles() {
|
|
const cycles = [];
|
|
const visited = new Set();
|
|
const walking = new Set();
|
|
const walk = (vertex) => {
|
|
if (!visited.has(vertex)) {
|
|
visited.add(vertex);
|
|
walking.add(vertex);
|
|
const it = this.neighbors(vertex);
|
|
for (let current = it.next(); true !== current.done; current = it.next()) {
|
|
const {value: neighbor} = current;
|
|
if (!visited.has(neighbor)) {
|
|
walk(neighbor);
|
|
}
|
|
else if (walking.has(neighbor)) {
|
|
cycles.push([vertex, neighbor]);
|
|
}
|
|
}
|
|
}
|
|
walking.delete(vertex);
|
|
};
|
|
const {tails} = this;
|
|
for (let current = tails.next(); true !== current.done; current = tails.next()) {
|
|
walk(current.value);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
ensureTail(tail) {
|
|
if (!this.arcs.has(tail)) {
|
|
this.arcs.set(tail, new Set());
|
|
}
|
|
return this.arcs.get(tail);
|
|
}
|
|
|
|
neighbors(vertex) {
|
|
return this.arcs.get(vertex).values();
|
|
}
|
|
|
|
sort() {
|
|
const visited = new Set();
|
|
const scores = new Map();
|
|
const walk = (vertex, score) => {
|
|
visited.add(vertex);
|
|
const neighbors = this.neighbors(vertex);
|
|
for (let current = neighbors.next(); true !== current.done; current = neighbors.next()) {
|
|
const {value: neighbor} = current;
|
|
if (!visited.has(neighbor)) {
|
|
score = walk(neighbor, score);
|
|
}
|
|
}
|
|
scores.set(vertex, score);
|
|
return score - 1;
|
|
};
|
|
let score = this.arcs.size - 1;
|
|
const {tails} = this;
|
|
for (let current = tails.next(); true !== current.done; current = tails.next()) {
|
|
const {value: vertex} = current;
|
|
if (!visited.has(vertex)) {
|
|
score = walk(vertex, score);
|
|
}
|
|
}
|
|
return Array.from(scores.entries())
|
|
.sort(([, l], [, r]) => l - r)
|
|
.map(([vertex]) => vertex);
|
|
}
|
|
|
|
removeDependency(head, tail) {
|
|
if (this.arcs.has(tail)) {
|
|
this.arcs.get(tail).delete(head);
|
|
}
|
|
}
|
|
|
|
get tails() {
|
|
return this.arcs.keys();
|
|
}
|
|
|
|
}
|