Skip to content

Commit e8be8e3

Browse files
committed
Implement Dikjstra's algorithm. Closes #13
1 parent a34eace commit e8be8e3

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

index.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = function Graph(serialized){
1515
outdegree: outdegree,
1616
depthFirstSearch: depthFirstSearch,
1717
topologicalSort: topologicalSort,
18+
shortestPath: shortestPath,
1819
serialize: serialize,
1920
deserialize: deserialize
2021
};
@@ -194,6 +195,91 @@ module.exports = function Graph(serialized){
194195
return depthFirstSearch(sourceNodes, includeSourceNodes).reverse();
195196
}
196197

198+
// Dijkstra's Shortest Path Algorithm.
199+
// Cormen et al. "Introduction to Algorithms" 3rd Ed. p. 658
200+
// Variable and function names correspond to names in the book.
201+
function shortestPath(source, destination){
202+
203+
// Upper bounds for shortest path weights from source.
204+
var d = {};
205+
206+
// Predecessors.
207+
var p = {};
208+
209+
// Poor man's priority queue, keyed on d.
210+
var q = {};
211+
212+
function initializeSingleSource(){
213+
nodes().forEach(function (node){
214+
d[node] = Infinity;
215+
});
216+
d[source] = 0;
217+
}
218+
219+
// Adds entries in q for all nodes.
220+
function initializePriorityQueue(){
221+
nodes().forEach(function (node){
222+
q[node] = true;
223+
});
224+
}
225+
226+
// Returns true if q is empty.
227+
function priorityQueueEmpty(){
228+
return Object.keys(q).length === 0;
229+
}
230+
231+
// Linear search to extract (find and remove) min from q.
232+
function extractMin(){
233+
var min = Infinity;
234+
var minNode;
235+
Object.keys(q).forEach(function(node){
236+
if (d[node] < min) {
237+
min = d[node];
238+
minNode = node;
239+
}
240+
});
241+
delete q[minNode];
242+
return minNode;
243+
}
244+
245+
function relax(u, v){
246+
var w = getEdgeWeight(u, v);
247+
if (d[v] > d[u] + w) {
248+
d[v] = d[u] + w;
249+
p[v] = u;
250+
}
251+
}
252+
253+
function dijkstra(){
254+
initializeSingleSource();
255+
initializePriorityQueue();
256+
while(!priorityQueueEmpty()){
257+
var u = extractMin();
258+
adjacent(u).forEach(function (v){
259+
relax(u, v);
260+
});
261+
}
262+
}
263+
264+
// Assembles the shortest path by traversing the
265+
// predecessor subgraph from destination to source.
266+
function path(){
267+
var nodeList = [];
268+
var node = destination;
269+
while(p[node]){
270+
nodeList.push(node);
271+
node = p[node];
272+
}
273+
nodeList.push(source);
274+
nodeList.reverse();
275+
return nodeList;
276+
}
277+
278+
dijkstra();
279+
280+
return path();
281+
}
282+
197283
// Serializes the graph.
198284
function serialize(){
199285
var serialized = {

test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,37 @@ describe("Graph", function() {
332332
});
333333

334334
});
335+
336+
describe("Dijkstra's Shortest Path Algorithm", function (){
337+
338+
it("Should compute shortest path on a single edge.", function (){
339+
var graph = Graph().addEdge("a", "b");
340+
assert.deepEqual(graph.shortestPath("a", "b"), ["a", "b"]);
341+
});
342+
343+
it("Should compute shortest path on two edges.", function (){
344+
var graph = Graph()
345+
.addEdge("a", "b")
346+
.addEdge("b", "c");
347+
assert.deepEqual(graph.shortestPath("a", "c"), ["a", "b", "c"]);
348+
});
349+
350+
it("Should compute shortest path on example from Cormen text (p. 659).", function (){
351+
var graph = Graph()
352+
.addEdge("s", "t", 10)
353+
.addEdge("s", "y", 5)
354+
.addEdge("t", "y", 2)
355+
.addEdge("y", "t", 3)
356+
.addEdge("t", "x", 1)
357+
.addEdge("y", "x", 9)
358+
.addEdge("y", "z", 2)
359+
.addEdge("x", "z", 4)
360+
.addEdge("z", "x", 6);
361+
assert.deepEqual(graph.shortestPath("s", "z"), ["s", "y", "z"]);
362+
assert.deepEqual(graph.shortestPath("s", "x"), ["s", "y", "x"]);
363+
});
364+
365+
});
335366
});
336367

337368
function contains(arr, item){

0 commit comments

Comments
 (0)