@graphty/algorithms
Version:
Graph algorithms library for browser environments implemented in TypeScript
129 lines • 4.53 kB
JavaScript
export function floydWarshall(graph) {
const nodes = Array.from(graph.nodes()).map((n) => n.id);
const distances = new Map();
const predecessors = new Map();
for (const node of nodes) {
distances.set(node, new Map());
predecessors.set(node, new Map());
const nodeDistances = distances.get(node);
const nodePredecessors = predecessors.get(node);
if (!nodeDistances || !nodePredecessors) {
continue;
}
for (const other of nodes) {
if (node === other) {
nodeDistances.set(other, 0);
nodePredecessors.set(other, null);
}
else {
nodeDistances.set(other, Infinity);
nodePredecessors.set(other, null);
}
}
}
for (const edge of Array.from(graph.edges())) {
const weight = edge.weight ?? 1;
const sourceDistances = distances.get(edge.source);
const sourcePredecessors = predecessors.get(edge.source);
if (sourceDistances && sourcePredecessors) {
sourceDistances.set(edge.target, weight);
sourcePredecessors.set(edge.target, edge.source);
}
if (!graph.isDirected) {
const targetDistances = distances.get(edge.target);
const targetPredecessors = predecessors.get(edge.target);
if (targetDistances && targetPredecessors) {
targetDistances.set(edge.source, weight);
targetPredecessors.set(edge.source, edge.target);
}
}
}
for (const k of nodes) {
for (const i of nodes) {
for (const j of nodes) {
const distancesI = distances.get(i);
const distancesK = distances.get(k);
const predecessorsI = predecessors.get(i);
const predecessorsK = predecessors.get(k);
if (!distancesI || !distancesK || !predecessorsI || !predecessorsK) {
continue;
}
const distIK = distancesI.get(k);
const distKJ = distancesK.get(j);
const distIJ = distancesI.get(j);
if (distIK === undefined || distKJ === undefined || distIJ === undefined) {
continue;
}
if (distIK + distKJ < distIJ) {
distancesI.set(j, distIK + distKJ);
const predKJ = predecessorsK.get(j);
predecessorsI.set(j, predKJ ?? null);
}
}
}
}
let hasNegativeCycle = false;
for (const node of nodes) {
const nodeDistances = distances.get(node);
if (!nodeDistances) {
continue;
}
const selfDistance = nodeDistances.get(node);
if (selfDistance !== undefined && selfDistance < 0) {
hasNegativeCycle = true;
break;
}
}
return {
distances,
predecessors,
hasNegativeCycle,
};
}
export function floydWarshallPath(graph, source, target) {
const result = floydWarshall(graph);
if (!graph.hasNode(source) || !graph.hasNode(target)) {
return null;
}
const sourceDistances = result.distances.get(source);
if (!sourceDistances) {
return null;
}
const distance = sourceDistances.get(target);
if (distance === undefined || distance === Infinity) {
return null;
}
const path = [];
let current = target;
while (current !== null && current !== source) {
path.unshift(current);
const sourcePredecessors = result.predecessors.get(source);
if (!sourcePredecessors) {
return null;
}
current = sourcePredecessors.get(current) ?? null;
if (path.length > graph.nodeCount) {
return null;
}
}
if (current === source) {
path.unshift(source);
return { path, distance };
}
return null;
}
export function transitiveClosure(graph) {
const result = floydWarshall(graph);
const closure = new Map();
for (const [source, distances] of Array.from(result.distances)) {
const reachable = new Set();
for (const [target, distance] of Array.from(distances)) {
if (distance < Infinity) {
reachable.add(target);
}
}
closure.set(source, reachable);
}
return closure;
}
//# sourceMappingURL=floyd-warshall.js.map