import { MinBinaryHeap } from '../../../data-structures/priority-queues' import { GraphNode, PrevVertices, ShortestDistances, WeightedGraph } from '../graph-node' /** * Perform Dijkstra's shortest path algorithm. * Find the shortest path between two vertices in a directed graph with positive weights. * * Time complexity: O[(V+E)logV] * Space complexity: O(V+E) * * @param {WeightedGraph} graph * @param {GraphNode} source * @param {GraphNode} target * @return {[Array<>, number]} */ export const shortestPath = ( graph: WeightedGraph, source: GraphNode, target: GraphNode ): [Array, number] => { const [dist, prev] = dijkstra(graph, source) const shortestPath = new Array() for (let at = target; at !== null; at = prev.get(at)!) { shortestPath.push(at.val) } return [shortestPath.reverse(), dist.get(target)!] } const dijkstra = ( graph: WeightedGraph, source: GraphNode ): [ShortestDistances, PrevVertices] => { const visited = new Set>() const dist: ShortestDistances = new Map() const prev: PrevVertices = new Map() for (const node of graph.keys()) { dist.set(node, Number.POSITIVE_INFINITY) prev.set(node, null) } dist.set(source, 0) // dist[source] needs to be 0, not infinity const pq = new MinBinaryHeap<[GraphNode, number]>([], (a, b) => a[1] - b[1]) pq.add([source, 0]) while (!pq.isEmpty()) { const [u, uDist] = pq.poll()! if (!u) throw new Error('This will never happen') const uDistStale = uDist > dist.get(u)! if (uDistStale) continue visited.add(u) const neighborsOfU = graph.get(u)! for (const [v, weight] of neighborsOfU) { if (visited.has(v)) continue const altDistance = uDist + weight if (altDistance < dist.get(v)!) { dist.set(v, altDistance) prev.set(v, u) pq.add([v, altDistance]) } } } return [dist, prev] }