Code implementation of the following guide for implementing the algorithm
https://isaaccomputerscience.org/concepts/dsa_search_dijkstra
Code implementation of the following guide for implementing the algorithm
https://isaaccomputerscience.org/concepts/dsa_search_dijkstra
| import { Graph } from "./graph"; | |
| describe("Graph", () => { | |
| let graph: Graph; | |
| beforeAll(() => { | |
| graph = new Graph(); | |
| graph.addNode("A"); | |
| graph.addNode("B"); | |
| graph.addNode("C"); | |
| graph.addNode("D"); | |
| graph.addNode("E"); | |
| graph.addNode("F"); | |
| graph.addEdge(["A", "B"], 3); | |
| graph.addEdge(["A", "C"], 5); | |
| graph.addEdge(["B", "D"], 3); | |
| graph.addEdge(["B", "C"], 3); | |
| graph.addEdge(["C", "D"], 3); | |
| graph.addEdge(["C", "E"], 6); | |
| graph.addEdge(["E", "F"], 1); | |
| graph.addEdge(["D", "F"], 10); | |
| }); | |
| it("should return most efficient path from A -> B", () => { | |
| expect(graph.findShortest("A", "B")).toEqual(["A", "B"]); | |
| }); | |
| it("should return most efficient path from A -> D", () => { | |
| expect(graph.findShortest("A", "D")).toEqual(["A", "B", "D"]); | |
| }); | |
| it("should return most efficient path from A -> F", () => { | |
| expect(graph.findShortest("A", "F")).toEqual(["A", "C", "E", "F"]); | |
| }); | |
| }); |
| import { find, minBy, pull, reverse } from "lodash"; | |
| import { Node, Edge } from "./types"; | |
| export class Graph { | |
| private nodes: Node[] = []; | |
| private edges: Edge[] = []; | |
| addNode(id: string) { | |
| this.nodes.push({ id }); | |
| } | |
| addEdge(nodes: string[], cost: number) { | |
| this.edges.push({ nodes, cost }); | |
| } | |
| /** | |
| * Calculates the shortest path based on cost from source to destination | |
| * @param source | |
| * @param destination | |
| */ | |
| findShortest(source: string, destination: string) { | |
| const visited = []; | |
| const unvisited = this.nodes.map(({ id }) => ({ | |
| node: id, | |
| cost: Infinity, | |
| previous: undefined, | |
| })); | |
| find(unvisited, { node: source }).cost = 0; | |
| while (unvisited.length > 0) { | |
| const currentNode = minBy(unvisited, "cost"); | |
| const costAdjustedNeighbours = this.getNeighbours(currentNode.node)?.map( | |
| ({ node, cost }) => ({ | |
| node, | |
| cost: cost + currentNode.cost, | |
| previous: currentNode.node, | |
| }), | |
| ); | |
| costAdjustedNeighbours.forEach(({ node, cost }) => { | |
| const neighbour = find(unvisited, { node }); | |
| if (neighbour && cost < neighbour.cost) { | |
| neighbour.cost = cost; | |
| neighbour.previous = currentNode.node; | |
| } | |
| }); | |
| visited.push(currentNode); | |
| pull(unvisited, currentNode); | |
| } | |
| const map = []; | |
| let current = find(visited, { node: destination }); | |
| while (!map.includes(source)) { | |
| map.push(current.node); | |
| current = find(visited, { node: current.previous }); | |
| } | |
| return reverse(map); | |
| } | |
| private getNeighbours(node: string) { | |
| return this.edges | |
| .filter(({ nodes }) => nodes?.includes(node)) | |
| .map(({ nodes, cost }) => ({ | |
| node: nodes.find((it) => it !== node), | |
| cost, | |
| })); | |
| } | |
| } |
| export type Node = { id: string }; | |
| export type Edge = { | |
| nodes: string[]; | |
| cost: number; | |
| }; |