Pseudocode
Pseudocode for Dijkstra's algorithm is provided below. Remember that the priority value of a vertex in the priority queue corresponds to the shortest distance we've found (so far) to that vertex from the starting vertex. Also, you can treat our priority queue as a min heap.
Add the starting vertex
s
to the initially empty fringe with priority value 0
Add all other vertices to the fringe with priority value of infinity
While the fringe is not empty:
Remove the vertex in the fringe with the minimum priority.
We'll call this vertex
For each of u
.
Its priority is the shortest distance from s
to u
.
u
's neighbors v
:
If
v
is not already in the priority queue, do nothing.
(We've already found the shortest distance from
Otherwise, update s
to v
.)
v
's predecessor to u
, and update its priority value to the minimum of:
Its current priority value
The shortest distance from
s
to u
+ the weight of the edge (u, v)
Calculating running time
Now let's calculate the running time of Dijkstra's algorithm using a binary min-heap priority queue as the fringe. Let
|E|
and |V|
be the number of edges and the number of vertices in the graph respectively. (In graph theory, E
and V
refer to the set of a graph's edges and the set of a graph's vertices; |E|
and |V|
are the sizes of these sets.)- Adding all
|V|
vertices of the graph to a binary min-heap takes a total ofO(|V|)
time. (Remember the linear time heapify example from lecture?) - Updating a vertex's priority value in the priority queue can be done with a binary min-heap by removing it, then readding it with an updated priority value. If we keep a hash map of vertices and their indices in the binary min-heap array and assume that the hash map
put
operation takes constant time, then we can remove and readd a vertex to a binary min-heap inO(log|V|)
time. This happens to a vertex at most once for each of its neighbors. Thus, this will happen at most|E|
times over the course of an entire run of Dijkstra's algorithm. Since each priority value update takesO(log|V|)
time, the total of all priority value updates takesO(|E|log|V|)
. - If we keep an adjacency matrix of edge weights, then we can access edge weights in constant time. If we keep a hash map of vertices with their priority values, then accessing a vertex's priority value is also a constant time operation. This makes calculating a vertex's updated priority value a constant time operation. Since we only have to calculate updated priority values
O(|E|)
times, all update calculations take a total ofO(|E|)
time. - Notice that each vertex is removed from the fringe exactly once, and never readded to the fringe (excluding priority value updates). Dijkstra's algorithm only removes from the priority queue
|V|
times, and each removal takesO(log|V|)
time for a total ofO(|V|log|V|)
time for all vertex removals. - Checking whether the priority queue is empty is a constaint time operation and happens
O(|V|)
times (once right before each vertex is removed from the priority queue). Thus, allisEmpty
checks take a total ofO(|V|)
time. - Iterating through a vertex's neighbors can be done in time proportional to that vertex's degree (the number of neighbors it has) with an adjacency list. Therefore iterating over all vertices' neighbors over the course of a run of Dijkstra's algorithm takes
O(|E|)
time.
Adding these running times together, we have
O(|E|log|V|)
for all priority value updates and O(|V|log|V|)
for removing all vertices (there are also some other O(|E|)
and O(|V|)
additive terms, but they are dominated by these two terms). This means the running time for Dijkstra's algorithm using a binary min-heap as a priority queue is O((|E|+|V|)log|V|)
.A directed graph is weakly connected if replacing all of its directed edges with undirected edges produces a connected undirected graph. If we assume that the input graph is weakly connected, then the graph must have at least
|V| - 1
edges. This implies that |V|
is in O(|E|)
, which means we can simplify the running time above to O(|E|log|V|)
.Fun fact: With a more advanced data structure called a Fibonacci heap, the running time of Dijkstra's algorithm can be reduced to
O(|E|+|V|log|V|)
Activity: Coding Dijkstra's algorithm
Add Dijkstra's algorithm to
Graph.java
from the previous lab. Here's the method header:public ArrayList<Integer> shortestPath(int startVertex, int endVertex) {
// YOUR CODE HERE
}
For this method, assume that each edge in the graph has a
myEdgeInfo
object that is an Integer
. Note: you do not have to implement the O((|E| + |V|)log|V|)
optimized version of Dijkstra's algorithm described above (although you can if you want a challenge).