Skip to content

Commit dc17f40

Browse files
committed
1.2 Practice. Add Dijkstra's shortest path
1 parent 1908fb0 commit dc17f40

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { dijkstraShortestPath } from '../src/practice/1-2-dijkstra-shortest-path';
2+
3+
const graph = {
4+
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
5+
edges: [
6+
{ source: 'a', destination: 'b', distance: 7 },
7+
{ source: 'a', destination: 'c', distance: 9 },
8+
{ source: 'a', destination: 'f', distance: 14 },
9+
{ source: 'b', destination: 'c', distance: 10 },
10+
{ source: 'b', destination: 'd', distance: 15 },
11+
{ source: 'c', destination: 'f', distance: 2 },
12+
{ source: 'c', destination: 'd', distance: 11 },
13+
{ source: 'd', destination: 'e', distance: 6 },
14+
{ source: 'e', destination: 'f', distance: 9 },
15+
],
16+
};
17+
18+
describe(dijkstraShortestPath.name, () => {
19+
it(`Should pass end-to-end test`, () => {
20+
const result = dijkstraShortestPath(graph, 'a', 'e');
21+
expect(result.distance).toBe(20);
22+
expect(result.nodeList).toEqual(['a', 'c', 'f', 'e']);
23+
});
24+
});

src/practice/0-undirected-graph.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export interface IEdge {
2+
source: string;
3+
destination: string;
4+
distance: number;
5+
}
6+
7+
export interface IGraph {
8+
nodes: string[];
9+
edges: IEdge[];
10+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { IGraph } from './0-undirected-graph';
2+
3+
export interface IPath {
4+
nodeList: string[];
5+
distance: number;
6+
}
7+
8+
export function dijkstraShortestPath(
9+
graph: IGraph,
10+
sourceNode: string,
11+
destinationNode: string,
12+
): IPath {
13+
const edges = new Set(graph.edges);
14+
const distanceTo = graph.nodes
15+
.reduce(
16+
(result, node) => (
17+
result[node] = {
18+
from: null,
19+
distance: node === sourceNode ? 0 : Number.MAX_SAFE_INTEGER,
20+
},
21+
result
22+
),
23+
{} as { [ dest: string ]: { from: string | null, distance: number } }
24+
);
25+
26+
const unvisited = new Set(graph.nodes);
27+
28+
let currentNode: string | undefined = sourceNode;
29+
do {
30+
unvisited.delete(currentNode);
31+
32+
for (let edge of edges)
33+
if (edge.source === currentNode || edge.destination === currentNode) {
34+
edges.delete(edge);
35+
36+
const neighbor = edge.source === currentNode ? edge.destination : edge.source;
37+
const alternativeDistance = distanceTo[currentNode].distance + edge.distance;
38+
39+
if (alternativeDistance < distanceTo[neighbor].distance) {
40+
distanceTo[neighbor] = { from: currentNode, distance: alternativeDistance };
41+
}
42+
}
43+
44+
// current = unvisited node with shortest `distance` path to it
45+
let closestUnvisitedEntry = (Object
46+
.entries(distanceTo)
47+
.sort((entry1, entry2) => entry1["1"].distance - entry2["1"].distance)
48+
.find(entry => unvisited.has(entry["0"])));
49+
currentNode = closestUnvisitedEntry && closestUnvisitedEntry["0"];
50+
51+
} while (currentNode);
52+
53+
return {
54+
distance: distanceTo[destinationNode].distance,
55+
nodeList: unwindPath(distanceTo, destinationNode),
56+
};
57+
}
58+
59+
export function unwindPath(
60+
distanceTo: { [dest: string]: { from: string | null, distance: number } },
61+
destinationNode: string,
62+
) {
63+
const path: string[] = [ destinationNode ];
64+
let currentNode: string | null = destinationNode;
65+
do {
66+
const entry: { from: string | null, distance: number } = distanceTo[currentNode];
67+
currentNode = entry.from;
68+
if (currentNode) path.unshift(currentNode);
69+
} while (currentNode);
70+
return path;
71+
}

0 commit comments

Comments
 (0)