Skip to content

Commit f400b71

Browse files
committed
Prints path along with weights and final distance
1 parent 8233583 commit f400b71

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/**
2+
* Dijkstra's Shortest Path Algorithm
3+
* Dijkstra's algorithm is greedy! That can cause problems!
4+
*
5+
*
6+
* Time Complexity: Time Complexity of Dijkstra's Algorithm is O ( V 2 )
7+
* but with min-priority queue it drops down to O ( V + E log V )
8+
*
9+
* @author Aditya Hajare <https://github.com/aditya43>
10+
*
11+
* IMPORTANT POINTS AND PSUDOCODE
12+
* -----------------------------------
13+
* 1. This function should accept a starting and ending vertex
14+
* 2. Create an object (we'll call it distances) and set each key to be every
15+
* vertex in the adjacency list with a value of infinity, except for the
16+
* starting vertex which should have a value of 0.
17+
* 3. After setting a value in the distances object, add each vertex with
18+
* a priority of Infinity to the priority queue, except the starting vertex,
19+
* which should have a priority of 0 because that's where we begin.
20+
* 4. Create another object called previous and set each key to be every
21+
* vertex in the adjacency list with a value of null
22+
* 5. Start looping as long as there is anything in the priority queue
23+
* - dequeue a vertex from the priority queue
24+
* - If that vertex is the same as the ending vertex - we are done!
25+
* - Otherwise loop through each value in the adjacency list at that vertex
26+
* - Calculate the distance to that vertex from the starting vertex
27+
* - if the distance is less than what is currently stored in our
28+
* distances object
29+
* - update the distances object with new lower distance
30+
* - update the previous object to contain that vertex
31+
* - enqueue the vertex with the total distance from the start node
32+
*
33+
* We can improve this algorithm by adding a heuristics (a best guess)
34+
*/
35+
class Vertex {
36+
constructor (vertex, weight) {
37+
this.node = vertex;
38+
this.weight = weight;
39+
}
40+
}
41+
42+
class Node {
43+
constructor (val, priority) {
44+
this.val = val;
45+
this.priority = priority;
46+
}
47+
}
48+
49+
class PriorityQueue {
50+
constructor () {
51+
this.values = [];
52+
}
53+
54+
enqueue (val, priority) {
55+
const node = new Node(val, priority);
56+
this.values.push(node);
57+
this.bubbleUp();
58+
}
59+
60+
dequeue () {
61+
const min = this.values[0];
62+
const end = this.values.pop();
63+
64+
if (this.values.length > 0) {
65+
this.values[0] = end;
66+
this.bubbleDown(0);
67+
}
68+
69+
return min;
70+
}
71+
72+
bubbleUp () {
73+
let idx = this.values.length - 1;
74+
75+
while (idx > 0) {
76+
const parentIdx = Math.floor((idx - 1) / 2);
77+
78+
if (this.values[idx].priority >= this.values[parentIdx].priority) {
79+
break;
80+
}
81+
82+
const tmp = this.values[parentIdx];
83+
this.values[parentIdx] = this.values[idx];
84+
this.values[idx] = tmp;
85+
86+
idx = parentIdx;
87+
}
88+
}
89+
90+
// Recursive
91+
bubbleDown (index) {
92+
const length = this.values.length;
93+
let largest = index;
94+
95+
const left = 2 * index + 1;
96+
const right = 2 * index + 2;
97+
98+
// if left child is greater than parent
99+
if (left <= length && this.values[left]) {
100+
if (this.values[left].priority < this.values[largest].priority) {
101+
largest = left;
102+
}
103+
}
104+
105+
// if right child is greater than parent
106+
if (right <= length && this.values[right]) {
107+
if (this.values[right].priority < this.values[largest].priority) {
108+
largest = right;
109+
}
110+
}
111+
112+
// swap
113+
if (largest !== index) {
114+
[this.values[largest], this.values[index]] = [this.values[index], this.values[largest]];
115+
this.bubbleDown(largest);
116+
}
117+
}
118+
119+
// Iterative
120+
bubbleDownIterative () {
121+
let idx = 0;
122+
const length = this.values.length;
123+
const element = this.values[0];
124+
while (true) {
125+
const leftChildIdx = 2 * idx + 1;
126+
const rightChildIdx = 2 * idx + 2;
127+
let leftChild, rightChild;
128+
let swap = null;
129+
130+
if (leftChildIdx < length) {
131+
leftChild = this.values[leftChildIdx];
132+
if (leftChild.priority > element.priority) {
133+
swap = leftChildIdx;
134+
}
135+
}
136+
if (rightChildIdx < length) {
137+
rightChild = this.values[rightChildIdx];
138+
if (
139+
(swap === null && rightChild.priority > element.priority) ||
140+
(swap !== null && rightChild.priority > leftChild.priority)
141+
) {
142+
swap = rightChildIdx;
143+
}
144+
}
145+
if (swap === null) break;
146+
this.values[idx] = this.values[swap];
147+
this.values[swap] = element;
148+
idx = swap;
149+
}
150+
}
151+
}
152+
153+
class WeightedGraph {
154+
constructor () {
155+
this.adjacencyList = {};
156+
}
157+
158+
addVertex (vertex) {
159+
if (!this.adjacencyList[vertex]) {
160+
this.adjacencyList[vertex] = [];
161+
}
162+
}
163+
164+
addEdge (vertex1, vertex2, weight) {
165+
this.adjacencyList[vertex1].push(new Vertex(vertex2, weight));
166+
this.adjacencyList[vertex2].push(new Vertex(vertex1, weight));
167+
}
168+
169+
Dijkstra (start, finish) {
170+
const nodes = new PriorityQueue();
171+
const distances = {};
172+
const previous = {};
173+
const paths = []; // to return at end
174+
let smallest;
175+
// build up initial state
176+
for (const vertex in this.adjacencyList) {
177+
if (vertex === start) {
178+
distances[vertex] = 0;
179+
nodes.enqueue(vertex, 0);
180+
} else {
181+
distances[vertex] = Infinity;
182+
nodes.enqueue(vertex, Infinity);
183+
}
184+
previous[vertex] = null;
185+
}
186+
// as long as there is something to visit
187+
while (nodes.values.length) {
188+
smallest = nodes.dequeue().val;
189+
if (smallest === finish) {
190+
// WE ARE DONE
191+
// BUILD UP PATH TO RETURN AT END
192+
while (previous[smallest]) {
193+
paths.push({ weight: distances[smallest], node: smallest });
194+
smallest = previous[smallest];
195+
}
196+
break;
197+
}
198+
if (smallest || distances[smallest] !== Infinity) {
199+
for (const neighbor in this.adjacencyList[smallest]) {
200+
// find neighboring node
201+
const nextNode = this.adjacencyList[smallest][neighbor];
202+
// calculate new distance to neighboring node
203+
const candidate = distances[smallest] + nextNode.weight;
204+
const nextNeighbor = nextNode.node;
205+
if (candidate < distances[nextNeighbor]) {
206+
// updating new smallest distance to neighbor
207+
distances[nextNeighbor] = candidate;
208+
// updating previous - How we got to neighbor
209+
previous[nextNeighbor] = smallest;
210+
// enqueue in priority queue with new priority
211+
nodes.enqueue(nextNeighbor, candidate);
212+
}
213+
}
214+
}
215+
}
216+
// return paths.concat({ weight: distances[smallest], node: smallest }).reverse();
217+
paths.push({ weight: distances[smallest], node: smallest });
218+
219+
let path = '';
220+
let distance = 0;
221+
222+
for (let i = paths.length - 1; i >= 0; i--) {
223+
path += paths[i].node + `(${paths[i].weight})`;
224+
distance += paths[i].weight;
225+
226+
if (i > 0) {
227+
path += ' -> ';
228+
}
229+
}
230+
231+
return {
232+
path,
233+
distance
234+
};
235+
}
236+
}
237+
238+
const graph = new WeightedGraph();
239+
240+
graph.addVertex('A');
241+
graph.addVertex('B');
242+
graph.addVertex('C');
243+
graph.addVertex('D');
244+
graph.addVertex('E');
245+
graph.addVertex('F');
246+
247+
graph.addEdge('A', 'B', 4);
248+
graph.addEdge('A', 'C', 2);
249+
graph.addEdge('B', 'E', 3);
250+
graph.addEdge('C', 'D', 2);
251+
graph.addEdge('C', 'F', 4);
252+
graph.addEdge('D', 'E', 3);
253+
graph.addEdge('D', 'F', 1);
254+
graph.addEdge('E', 'F', 1);
255+
256+
console.log(graph.Dijkstra('A', 'E'));

0 commit comments

Comments
 (0)