CS310 Notes 06 Dijkstra Algorithm for SSSP
CS310 Notes 06 Dijkstra Algorithm for SSSP
Contents
1 Weighted Graphs 1
1.1 Weighted paths and shortest paths . . . . . . . . . . . . . . . . . . . . . . 3
3 Dijkstra’s Algorithm 5
3.1 Dijkstra’s Algorithm for Distances . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 Dijkstra’s Algorithm for Shortest Path . . . . . . . . . . . . . . . . . . . . 8
3.3 Proof of Correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.4 Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1 Weighted Graphs
So far, we have looked at graphs where edges are assumed to be uniformly weighted i.e.
that their weights relative to other edges in the graph are the same, hence the absense
of weights. However, there are certain situations where such a graph is unsuitable for
modelling the problem. For example, consider modelling maps such that vertices represent
nodes or locations and weights represent distances between them. There are several real-
world scenarios which are modelled as weighted graphs. For instance:
water networks with weights representing water capacity of pipes
electrical circuits with weights representing resistance or maximum voltage or cur-
rent
computer or phone networks with weights representing length of wires between nodes
1
3.5km
2km
2km 4.8km 5km
5km
6km
Hence, we now define a new type of graph, one where edges have a weight associated with
them, also known as the cost. A weighted graph is defined as a graph G = (V, E) where
V is the set of its vertices and E is the set of its edges, and a cost or weight function w,
defined as w : E → R where e ∈ E. Alternatively, we may define a set W of pairs (e, w)
where e ∈ E and w is the associated cost with e. Throughtout, we consider |V | = n and
|E| = m.
3 4
S A B
4 8 6
5
9 8
5 3
C E F
3 3
2 12
D 14 G
Weighted graphs can be represented as adjacency list or adjacency matrix. The adjacency
list of vertex u is now a list of (key,value) pairs where the key is the adjacent vertex v and
the value is the weight of the edge (u, v). In the adjacency matrix M , the entry M [i][j]
is the weight of the edge (i, j). Note that for undirected graphs, the adjacency matrix
2
A 3 C 4 D 9
S A B C D E F G
S
S 0 3 0 4 9 0 0 0
A B 4
A 0 0 4 0 0 0 0 0
B E 6 G 8
B 0 0 0 0 0 6 0 8
C ..
D C .
E
D
E
F
F
G
G
would be symmetric, and for a graph where no vertex has a self-loop, the diagonal of the
matrix of is all zeros as w(u, u) = 0.
3
3 4
S A B
Three S − G paths
4 8 6
5 C(p1 ) = 3 + 4 + 8
9 8
5 3
C E F C(p2 ) = 4 + 5 + 3 + 3
3 3
2 12 C(p3 ) = 9 + 14
D 14 G
Figure 5: Using BFS to solve single source shortest path problem for weighted graphs
This seems like a correct solution at first, but what if weights are not integers or negative?
This solution would not work then. Even if we limit the input to graphs having positive
integer weights, what if the weights are of the order of millions? An edge of weight 1
million would have to be replaced by 1 million dummy vertices and edges. This would
blow up the size of the graph and given that running time of BFS is O(n + m), this is
an infeasible algorithm. Therefore, we need another solution to the single source shortest
path problem, i.e. Dijkstra’s algorithm for shortest path.
4
3 Dijkstra’s Algorithm
Next, with the definitions of weighted graphs, shortest paths in such graphs, and mo-
tivation for a solution other than BFS for the single source shortest path problem in
weighted graphs, we now discuss an algorithm for it. The algorithm is known as Dijk-
stra’s algorithm, proposed by Edsger W. Dijkstra. It works for a weighted graph with
non-negative weights, and builds a shortest-path tree rooted at s, provided the vertices
are connected i.e. have atleast one path between them.
Assumptions:
Given a weighted graph G = (V, E) and two vertices s, v ∈ V where s is the source vertex,
and v is to be the destination vertex, the problem is to find the shortest path from s to
t. We make the following assumptions:
All vertices are reachable from s, otherwise there would be no shortest path from s
to the unreachable vertex, i.e. distance = ∞. By running a BFS/DFS rooted at s,
we can determine if v is reachable from s, ∀vinV . If so, then we proceed as it is. If
not, we can dimply remove the unreachable vertices.
All edge weights are non-negative. This is required for Dijkstra’s algorithm to work.
Bellman-Ford algorithm deals with negative weights.
5
One vertex is iteratively added to R from R̄ until R̄ = ∅ and R = V . The main question
now is, which vertex from R̄ should be added to R? The vertex closest to s should be
added. Such a vertex must be at the ‘frontier’ of R̄. Why must this be true?
Let v the closest to s in R̄, and consider the shortest path s, . . . , u, v. Since w(uv) ≥ 0,
u must be closer to s than v. This implies that u must be in R, because otherwise it will
contradict v being closest to s in R̄. This shows that v is only one edge away from R, and
that edge is (u, v). Therefore, we restrict the search of the closest vertex to s to ‘single
edge extensions’ of paths to u ∈ R.
In order to determine the closest vertex in R̄ to s, the algorithm assigns a score to each
crossing edge. An edge e = (u, v) is crossing if u ∈ R and v ∈ R̄. Score is defined as:
d(u) + w(u, v) for (u, v) ∈ E, u ∈ R, v ∈
/ R. The vertex added to R in each iteration is a
frontier vertex adjacent through a crossing edge with minimum score.
6
Pseudocode:
Algorithm Dijkstra’s Algorithm for distances from s to all vertices
d[1 . . . n] ← [∞ . . . ∞]
d[s] ← 0 R ← {s}
while R ̸= V do
Select e = (u, v), u ∈ R, v ∈
/ R, with minimum d[u] + w(uv)
R ← R ∪ {v}
d[v] ← d[u] + w(uv)
Example Run:
d d
3 4 S 0 3 4 S 0
S A B S A B
A ∞ A 3
4 8 B ∞ 4 8 B ∞
6 6
5 C ∞ 5 C ∞
9 8 9 8
5 3 D ∞ 5 3 D ∞
C E F C E F
3 3 E ∞ 3 3 E ∞
2 12 F ∞ 2 12 F ∞
D 14 G D 14 G
G ∞ G ∞
d d
3 4 S 0 3 4 S 0
S A B S A B
A 3 A 3
4 8 B ∞ 4 8 B 7
6 6
5 C 4 5 C 4
9 8 9 8
5 3 D ∞ 5 3 D ∞
C E F C E F
3 3 E ∞ 3 3 E ∞
2 12 F ∞ 2 12 F ∞
D 14 G D 14 G
G ∞ G ∞
d d
3 4 S 0 3 4 S 0
S A B S A B
A 3 A 3
4 8 B 7 4 8 B 7
6 6
5 C 4 5 C 4
9 8 9 8
5 3 D 7 5 3 D 9
C E F C E F
3 3 E ∞ 3 3 E 9
2 12 F ∞ 2 12 F ∞
D 14 G D 14 G
G ∞ G ∞
d d
3 4 S 0 3 4 S 0
S A B S A B
A 3 A 3
4 8 B 7 4 8 B 7
6 6
5 C 4 5 C 4
9 8 9 8
5 3 D 9 5 3 D 9
C E F C E F
3 3 E 9 3 3 E 9
2 12 F 12 2 12 F 12
D 14 G D 14 G
G ∞ G 15
7
3.2 Dijkstra’s Algorithm for Shortest Path
In order to find the shortest paths, and not just distances from s to all other vertices,
we need to do some extra book-keeping, i.e. record predecessor relationships so that the
source of each edge used is known. We set up another array P , which holds the vertex via
which we came to that particular vertex. For all vertices, this is set to null at the start.
To produce the shortest path to a vertex t from s, one can simply backtrack through
the array P , starting at P [t] and moving to the index indicated till s is reached. The
pseudocode is as follows:
d prev
3 4 S 0 N il
S A B
A 3 S
4 8 B 7 A
6
5 C 4 S
9 8
5 3 D 9 C
C E F
3 3 E 9 D
2 12 F 12 E
D 14 G G 15 B
As evident from Figure 7, Dijkstra’s implicitly builds a tree when predecessor relationships
are recorded. Note that all vertices are connected, there is no cycle and there are n − 1
edges, i.e. all the properties that define a tree.
8
Consider the following example in Figure 8.
4
B
−2
A
3
C
Figure 8: Dijkstra with source A adds greedily the vertex C and B to R in this order and
selects the paths AC and AB as shortest paths from S to B and C. While clearly the
shortest path from A to C is the path ABC of total weight 2.
This immediately gives us that if this algorithm is correct, its proof of correctness better
use critically the fact that there are no negative weight edges.
Correctness of Dijkstra’s algorithm follows from the following theorem.
Theorem 1. In each iteration i of the Dijkstra’s algorithm, ∀ u ∈ R, d(u) is equal to the
length of a shortest path from s to u.
s u v
s−v path P selected by the algorithm
∈R ∈R̄
z}|{
z }| {
s x y v
s−v path P 0 6= P
9
Note that the prefix of the path from s to u are all vertices in R at this stage of the
algorithm and only v ∈ R̄.
We will show that every other path from s to v is at least as long as P
Let P ′ be any other path in G from s to v, classifying vertices of P ′ according to R and R̄
at this iteration of the algorithm, we know that s ∈ R and v ∈ R̄, hence this path starts
in R and must cross to R̄ (may be many times). Consider the first time it crosses R, i.e.
let (x, y) be the first edge in P ′ such that x ∈ R and y ∈ R̄.
∈R
z }| {
s u v
s−v path P selected by the algorithm
∈R ∈R̄
z }| { z}|{
s x y v
s−v path P 0 6= P
∈R
z }| {
s u v
s−v path P selected by the algorithm
≥ d[x]=d(s,x) w(xy)≥0 ≥0
z }| { z }| { z }| {
s x y v
s−v path P 0 6= P
To finish the proof we use how the algorithm selected the edge (u, v). At this iteration,
since x ∈ R and y ∈ R̄ and also u ∈ R and v ∈ R̄. So certainly the edge (x, y) was
10
a candidate to be selected as a crossing edge. The algorithm selected a crossing edge
minimizing the score, so the fact that (u, v) was selected and (x, y) was not mean that
d(u) + w(uv) ≤ d(x) + w(xy).
Combining this with the above we get that l(P ′ ) ≥ d(x) + w(xy) ≥ d(u) + w(uv) = l(P ),
hence l(P ′ ) ≥ l(P ) for any path P ′ from s to v.
3.4 Runtime
We now analyze the runtime of this implementation of Dijkstra’s algorithm. We can see
that the while loop will run for O(n) time, since it will stop when R = V , and a vertex
is added to R at each vertex. Inside the while loop, we try to find the edge that gives
us the least path cost. Since we will look at all the edges in the worst case to find the
minimum, we would O(m) time to do so. Overall, this gives us a total asymptotic time
bound of O(nm).
11
d R R̄
s 0 a 1 key = 8 + 1
2
a 8 key = min{8 + 2, 5 + 3}
b 7 s 3
1
key = 5 + 1
c 5 1
3
d ∞
e ∞
c 4
key = min{5 + 4, 7 + 5}
5
f ∞
g ∞ b 6 key = 7 + 6
key = ∞
Consider the state during a run of Dijkstra’s implemented using vertex centric approach
shown in Figure 9. The vertex currently with the minimum key is x, and hence x would
be the vertex to be added to R in this iteration. Then, only keys of vertices adjacent to
x need to be updated. The new key of y would be min(5 + 4, 7 + 5, 6 + 3) and the new
key of z would be min(8 + 2, 5 + 3, 6 + 1). The vertex that would be added in the next
iteration is z as it would have the minimum key, i.e. 7.
12
Pseudocode:
Runtime:
Analyzing the runtime of the vertex-centric Dijkstra’s using the pseudocode above, we see
that the outer while loop still runs O(n) times. However, finding the minimum score vertex
takes O(n) time, as opposed to the O(m) time to find the minimum score crossing edge in
the previous implementation. Since we need to update the neighbors of the added vertex
only, it takes O(n) time at max. Therefore, the overall runtime is O(n2 + m) = O(n2 ).
This is better than the last implementation, specially for dense graphs where the number
of edges is large (about n2 ). However, quadratic runtime is still infeasible for practical
purposes and large graphs. As always, we ask if we can do any better. As it turns out,
we can do much better using a data structure more suited to our problem of repeatedly
finding the minimum key vertex, which is expensive, namely the min-heap.
13
implementing Dijkstra’s Algorithm.
14
The idea follows from the vertex-centric approach. Each vertex v is assigned a key which is
the current known distance from the source s to v. The n vertices are inserted into a min-
heap H. In each iteration, ExtractMin(H) is used to get the vertex with the minimum
key, suppose u, which is to be added to R. Next, the neighborhood {v|(u, v) ∈ E} of the
extracted vertex u is traversed. The key of a neighboring vertex v is updated if the length
of the new path p = s, · · · , u, v is less than the current key value of v using the operation
DecreaseKey(H,v,len(p)).
Pseudocode:
Algorithm Dijkstra’s Algorithm for distances from s to all vertices using MinHeap
d[1 . . . n] ← [∞ . . . ∞]
d[s] ← 0
H ← Initialize(V, d) ▷ make a heap with all vertices and keys as d[·]
while R ̸= V do
v ← extractMin(H)
for each z ∈ N (v) ∩ R do
if d[z] > d[v] + w(vz) then
DecreaseKey(H, z, d[v] + w(vz))
R ← R ∪ {v}
Runtime:
In order to analyze the runtime of this heap-based implementation of Dijkstra’s, we will
count the number of calls to the ExtractMin and DecreaseKey operations as the
rest of the work in the algorithm takes constant time.
In total, there are n ExtractMin operations
On extracting v, there are O(deg(v)) DecreaseKey operations
In total, there are v∈V deg(v) = m DecreaseKey operations.
P
15