UNPKG

@graphty/algorithms

Version:

Graph algorithms library for browser environments implemented in TypeScript

131 lines 4.36 kB
/** * 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; } /** * Reconstruct path from predecessor map */ function reconstructPath(target, predecessors) { const path = []; let current = target; while (current !== null) { path.unshift(current); current = predecessors.get(current) ?? null; } return path; } //# sourceMappingURL=bellman-ford.js.map