@graphty/algorithms
Version:
Graph algorithms library for browser environments implemented in TypeScript
120 lines • 4.14 kB
JavaScript
import { reconstructPath } from "../../utils/graph-utilities.js";
/**
* Find shortest paths from source using Bellman-Ford algorithm
*/
export function bellmanFord(graph, source, options = {}) {
if (!graph.hasNode(source)) {
throw new Error(`Source node ${String(source)} not found in graph`);
}
const distances = new Map();
const predecessors = new Map();
const nodes = Array.from(graph.nodes()).map((node) => node.id);
// Initialize distances
for (const nodeId of nodes) {
distances.set(nodeId, nodeId === source ? 0 : Infinity);
predecessors.set(nodeId, null);
}
// Relax edges |V| - 1 times
for (let i = 0; i < nodes.length - 1; i++) {
let updated = false;
for (const edge of Array.from(graph.edges())) {
const u = edge.source;
const v = edge.target;
const weight = edge.weight ?? 1;
const distanceU = distances.get(u);
const distanceV = distances.get(v);
if (distanceU !== undefined && distanceV !== undefined && distanceU !== Infinity) {
const newDistance = distanceU + weight;
if (newDistance < distanceV) {
distances.set(v, newDistance);
predecessors.set(v, u);
updated = true;
// Early termination if target reached
if (options.target && v === options.target) {
break;
}
}
}
}
// If no update in this iteration, we can stop early
if (!updated) {
break;
}
}
// Check for negative cycles
const negativeCycleNodes = [];
let hasNegativeCycle = false;
for (const edge of graph.edges()) {
const u = edge.source;
const v = edge.target;
const weight = edge.weight ?? 1;
const distanceU = distances.get(u);
const distanceV = distances.get(v);
if (distanceU !== undefined && distanceV !== undefined && distanceU !== Infinity) {
const newDistance = distanceU + weight;
if (newDistance < distanceV) {
hasNegativeCycle = true;
negativeCycleNodes.push(v);
}
}
}
return {
distances,
predecessors,
hasNegativeCycle,
negativeCycleNodes,
};
}
/**
* Find shortest path between two specific nodes using Bellman-Ford
*/
export function bellmanFordPath(graph, source, target) {
if (!graph.hasNode(source)) {
throw new Error(`Source node ${String(source)} not found in graph`);
}
if (!graph.hasNode(target)) {
throw new Error(`Target node ${String(target)} not found in graph`);
}
const result = bellmanFord(graph, source, { target });
if (result.hasNegativeCycle) {
throw new Error("Graph contains a negative cycle");
}
const distance = result.distances.get(target);
if (distance === undefined || distance === Infinity) {
return null; // No path exists
}
// Reconstruct path
const path = reconstructPath(target, result.predecessors);
return {
distance,
path,
predecessor: result.predecessors,
};
}
/**
* Check if graph has negative cycles using Bellman-Ford
*/
export function hasNegativeCycle(graph) {
const nodes = Array.from(graph.nodes());
if (nodes.length === 0) {
return false;
}
// Need to check from multiple sources in case of disconnected components
const checked = new Set();
for (const node of nodes) {
if (!checked.has(node.id)) {
const result = bellmanFord(graph, node.id);
if (result.hasNegativeCycle) {
return true;
}
// Mark all reachable nodes as checked
for (const [nodeId, distance] of Array.from(result.distances)) {
if (distance !== Infinity) {
checked.add(nodeId);
}
}
}
}
return false;
}
//# sourceMappingURL=bellman-ford.js.map