UNPKG

@graphty/algorithms

Version:

Graph algorithms library for browser environments implemented in TypeScript

189 lines 6.12 kB
/** * Breadth-First Search (BFS) implementation * * Explores graph level by level from a starting node. Guarantees shortest path * in unweighted graphs and can be used for various graph analysis tasks. */ /** * Perform breadth-first search starting from a given node */ export function breadthFirstSearch(graph, startNode, options = {}) { if (!graph.hasNode(startNode)) { throw new Error(`Start node ${String(startNode)} not found in graph`); } const visited = new Set(); const queue = []; const order = []; const tree = new Map(); // Initialize with start node queue.push({ node: startNode, level: 0 }); visited.add(startNode); tree.set(startNode, null); while (queue.length > 0) { const current = queue.shift(); if (!current) { break; } order.push(current.node); // Call visitor callback if provided if (options.visitCallback) { options.visitCallback(current.node, current.level); } // Early termination if target found if (options.targetNode && current.node === options.targetNode) { break; } // Explore neighbors for (const neighbor of graph.neighbors(current.node)) { if (!visited.has(neighbor)) { visited.add(neighbor); tree.set(neighbor, current.node); queue.push({ node: neighbor, level: current.level + 1 }); } } } return { visited, order, tree }; } /** * Find shortest path between two nodes in an unweighted graph using BFS */ export function shortestPathBFS(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`); } // Special case: source equals target if (source === target) { return { distance: 0, path: [source], predecessor: new Map([[source, null]]), }; } const visited = new Set(); const queue = []; const predecessor = new Map(); // Initialize BFS queue.push({ node: source, distance: 0 }); visited.add(source); predecessor.set(source, null); while (queue.length > 0) { const current = queue.shift(); if (!current) { break; } // Target found if (current.node === target) { const path = reconstructPath(target, predecessor); return { distance: current.distance, path, predecessor, }; } // Explore neighbors for (const neighbor of graph.neighbors(current.node)) { if (!visited.has(neighbor)) { visited.add(neighbor); predecessor.set(neighbor, current.node); queue.push({ node: neighbor, distance: current.distance + 1 }); } } } // No path found return null; } /** * Find shortest paths from source to all reachable nodes using BFS */ export function singleSourceShortestPathBFS(graph, source) { if (!graph.hasNode(source)) { throw new Error(`Source node ${String(source)} not found in graph`); } const results = new Map(); const visited = new Set(); const queue = []; const predecessor = new Map(); // Initialize BFS queue.push({ node: source, distance: 0 }); visited.add(source); predecessor.set(source, null); while (queue.length > 0) { const current = queue.shift(); if (!current) { break; } // Store result for current node const path = reconstructPath(current.node, predecessor); results.set(current.node, { distance: current.distance, path, predecessor: new Map(predecessor), }); // Explore neighbors for (const neighbor of graph.neighbors(current.node)) { if (!visited.has(neighbor)) { visited.add(neighbor); predecessor.set(neighbor, current.node); queue.push({ node: neighbor, distance: current.distance + 1 }); } } } return results; } /** * Check if the graph is bipartite using BFS coloring */ export function isBipartite(graph) { if (graph.isDirected) { throw new Error("Bipartite test requires an undirected graph"); } const color = new Map(); const visited = new Set(); // Check each connected component for (const node of Array.from(graph.nodes())) { if (!visited.has(node.id)) { const queue = [node.id]; color.set(node.id, 0); visited.add(node.id); while (queue.length > 0) { const current = queue.shift(); if (!current) { break; } const currentColor = color.get(current); if (currentColor === undefined) { continue; } for (const neighbor of Array.from(graph.neighbors(current))) { if (!visited.has(neighbor)) { // Color with opposite color color.set(neighbor, currentColor === 0 ? 1 : 0); visited.add(neighbor); queue.push(neighbor); } else if (color.get(neighbor) === currentColor) { // Same color as current node - not bipartite return false; } } } } } return true; } /** * Reconstruct path from predecessor map */ function reconstructPath(target, predecessor) { const path = []; let current = target; while (current !== null) { path.unshift(current); current = predecessor.get(current) ?? null; } return path; } //# sourceMappingURL=bfs.js.map