KEMBAR78
graph theory algorithms, graph theory algorithms | PDF
Graph Theory
Video Series
Graph Theory
Intro & Overview
William Fiset
Brief introduction
Graph theory is the mathematical theory
of the properties and applications of
graphs (networks).
The goal of this series is to gain an
understanding of how to apply graph
theory to real world applications.
Brief introduction
A graph theory problem might be:
Given the constraints above, how many different
sets of clothing can I make by choosing an article
from each category?
Brief introduction
The canonical graph theory example is
a social network of friends.
This enables interesting questions such as: how many
friends does person X have? Or how many degrees of
separation are there between person X and person Y?
Types of
Graphs
Undirected Graph
An undirected graph is a graph in which edges
have no orientation. The edge (u, v) is
identical to the edge (v, u). - Wiki
Undirected Graph
An undirected graph is a graph in which edges
have no orientation. The edge (u, v) is
identical to the edge (v, u). - Wiki
B
A
C
D
E
F
In the graph above, the nodes could
represent cities and an edge could
represent a bidirectional road.
Directed Graph (Digraph)
A directed graph or digraph is a graph
in which edges have orientations. For
example, the edge (u, v) is the edge
from node u to node v.
Directed Graph (Digraph)
A
D
C
B
F
E
In the graph above, the nodes could represent
people and an edge (u, v) could represent
that person u bought person v a gift.
A directed graph or digraph is a graph
in which edges have orientations. For
example, the edge (u, v) is the edge
from node u to node v.
Weighted Graphs
Many graphs can have edges that contain a
certain weight to represent an arbitrary value
such as cost, distance, quantity, etc…
B
A
C
D
E
F
4
4
8
2
3
1
11
2
9
NOTE: I will usually denote an edge of such a
graph as a triplet (u, v, w) and specify
whether the graph is directed or undirected.
Special Graphs
Trees!
A tree is an undirected connected graph with
no cycles. Equivalently, it is a connected
graph with N nodes and N-1 edges.
Rooted Trees!
A rooted tree is a tree with a designated root
node where every edge either points away from
or towards the root node. When edges point
away from the root the graph is called an
arborescence (out-tree) and anti-arborescence
(in-tree) otherwise.
In-tree Out-tree Out-tree
Directed Acyclic Graphs (DAGs)
DAGs are directed graphs with no cycles.
These graphs play an important role in
representing structures with dependencies.
Several efficient algorithms exist to
operates on DAGs.
Cool fact: All out-trees are DAGs but not
all DAGs are out-trees.
Bipartite Graph
A bipartite graph is one whose vertices can be
split into two independent groups U, V such
that every edge connects betweens U and V.
Other definitions exist such as: The
graph is two colourable or there is no
odd length cycle.
Complete Graphs
A complete graph is one where there is a
unique edge between every pair of nodes.
A complete graph with n vertices is
denoted as the graph Kn.
K1 K2 K3 K4 K5 K6
Representing
Graphs
Adjacency Matrix
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
A adjacency matrix m is a very simple way to
represent a graph. The idea is that the cell
m[i][j] represents the edge weight of going
from node i to node j.
NOTE: It is often assumed that the edge of
going from a node to itself has a cost of zero.
Adjacency Matrix
Pros Cons
Space efficient for
representing dense graphs
Requires Θ(V²) space
Edge weight lookup is O(1)
Iterating over all edges
takes Θ(V²) time
Simplest graph
representation
Adjacency List
An adjacency list is a way to represent a
graph as a map from nodes to lists of edges.
A -> [(B,4),(C,1)]
B -> [(C,6)]
C -> [(A,4),(B,1),(D,2)]
D -> []
A
C
B
D
4
1 6
4 1
2
A -> [(B,4),(C,1)]
B -> [(C,6)]
C -> [(A,4),(B,1),(D,2)]
D -> []
Adjacency List
A
C
B
D
4
1 6
4 1
2
Node C can reach
Node A with cost 4
Node B with cost 1
Node D with cost 2
An adjacency list is a way to represent a
graph as a map from nodes to lists of edges.
Adjacency List
Pros Cons
Space efficient for
representing sparse graphs
Less space efficient for
denser graphs.
Iterating over all edges
is efficient
Edge weight lookup is O(E)
Slightly more complex
graph representation
Edge List
This representation is seldomly used because
of its lack of structure. However, it is
conceptually simple and practical in a
handful of algorithms.
[(C,A,4), (A,C,1),
(B,C,6), (A,B,4),
(C,B,1), (C,D,2)]
A
C
B
D
4
1 6
4 1
2
An edge list is a way to represent a graph
simply as an unordered list of edges. Assume
the notation for any triplet (u,v,w) means:
“the cost from node u to node v is w”
Pros Cons
Space efficient for
representing sparse graphs
Less space efficient for
denser graphs.
Iterating over all edges
is efficient
Edge weight lookup is O(E)
Very simple structure
Edge List
Next Video: Graph Theory Problems
Graph Theory
Series
William
Fiset
Graph Theory
Video Series
Problems in
Graph Theory
William Fiset
Common Graph Theory Problems
Is the graph directed or undirected?
Are the edges of the graph weighted?
Is the graph I will encounter likely to be
sparse or dense with edges?
Should I use an adjacency matrix, adjacency
list, an edge list or other structure to
represent the graph efficiently?
For the upcoming problems ask yourself:
Shortest path problem
Algorithms: BFS (unweighted graph), Dijkstra’s,
Bellman-Ford, Floyd-Warshall, A*, and many more.
Given a weighted graph, find the shortest
path of edges from node A to node B.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
11
11
4
Start End
Algorithms: BFS (unweighted graph), Dijkstra’s,
Bellman-Ford, Floyd-Warshall, A*, and many more.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
11
11
4
Start End
Shortest path problem
Given a weighted graph, find the shortest
path of edges from node A to node B.
Connectivity
Typical solution: Use union find data
structure or any search algorithm (e.g DFS).
Does there exist a path between
node A and node B?
Connectivity
Typical solution: Use union find data
structure or any search algorithm (e.g DFS).
Does there exist a path between
node A and node B?
Negative cycles
Algorithms: Bellman-Ford and Floyd-Warshall
Does my weighted digraph have any negative
cycles? If so, where?
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
3
Unaffected
node
Directly in
negative cycle
Negative cycles
Algorithms: Bellman-Ford and Floyd-Warshall
Does my weighted digraph have any negative
cycles? If so, where?
Unaffected
node
Directly in
negative cycle
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
3
Strongly Connected Components
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
Algorithms: Tarjan’s and Kosaraju's algorithm
Strongly Connected Components
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
Algorithms: Tarjan’s and Kosaraju's algorithm
Traveling Salesman Problem
Algorithms: Held-Karp, branch and bound and
many approximation algorithms
"Given a list of cities and the distances between each
pair of cities, what is the shortest possible route that
visits each city exactly once and returns to the origin
city?” - Wiki
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
Traveling Salesman Problem
Algorithms: Held-Karp, branch and bound and
many approximation algorithms
"Given a list of cities and the distances between each
pair of cities, what is the shortest possible route that
visits each city exactly once and returns to the origin
city?” - Wiki
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
A
C
B
D
9
3
1
-4
Traveling Salesman Problem
Algorithms: Held-Karp, branch and bound and
many approximation algorithms
The TSP problem is NP-Hard meaning it’s a very
computationally challenging problem. This is unfortunate
because the TSP has several very important applications.
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
A
C
B
D
9
3
1
-4
Bridges
A bridge / cut edge is any edge in a
graph whose removal increases the number
of connected components.
0
1
2
5
3
6
4
8
7
Bridges
A bridge / cut edge is any edge in a
graph whose removal increases the number
of connected components.
0
1
2
5
3
6
4
8
7
Bridges are important in graph theory because they
often hint at weak points, bottlenecks or
vulnerabilities in a graph.
Articulation points
An articulation point / cut vertex is any
node in a graph whose removal increases
the number of connected components.
0
1
2
5
3
6
4
8
7
Articulation points
An articulation point / cut vertex is any
node in a graph whose removal increases
the number of connected components.
0
1
2
5
3
6
4
8
7
Articulation points are important in graph theory
because they often hint at weak points, bottlenecks
or vulnerabilities in a graph.
A
E
D
F G
B
H
I
C
J
5
2
9
1
2
1
5
7
4
6
1
0
8
4
4
2
1
1
Minimum Spanning Tree (MST)
Algorithms: Kruskal’s, Prim’s & Borůvka's algorithm
A minimum spanning tree (MST) is a subset of
the edges of a connected, edge-weighted graph
that connects all the vertices together,
without any cycles and with the minimum
possible total edge weight. - Wiki
Algorithms: Kruskal’s, Prim’s & Borůvka's algorithm
A minimum spanning tree (MST) is a subset of
the edges of a connected, edge-weighted graph
that connects all the vertices together,
without any cycles and with the minimum
possible total edge weight. - Wiki
A
E
D
F G
B
H
I
C
J
2
1
2
1
1
0
4
2
1
This MST has a
total weight of
14. Note that MSTs
on a graph are not
always unique.
Minimum Spanning Tree (MST)
Algorithms: Kruskal’s, Prim’s & Borůvka's algorithm
MSTs are seen in many applications including:
Designing a least cost network, circuit
design, transportation networks, and etc…
A
E
D
F G
B
H
I
C
J
2
1
2
1
1
0
4
2
1
Minimum Spanning Tree (MST)
This MST has a
total weight of
14. Note that MSTs
on a graph are not
always unique.
Network flow: max flow
Algorithms: Ford-Fulkerson, Edmonds-Karp & Dinic’s algorithm
1
2
1
8
4
2
1
3
4
5
6
3
3
7
1
4
3
8
Source Sink
Q: With an infinite input source how much “flow”
can we push through the network?
Suppose the edges are roads with cars, pipes with
water or hallways with packed with people. Flow
represents the volume of water allowed to flow through
the pipes, the number of cars the roads can sustain in
traffic and the maximum amount of people that can
navigate through the hallways.
Next Video: Depth First Search
Problems in
Graph Theory
Graph Theory
Video Series
Graph Theory:
Depth First Search
William Fiset
DFS overview
The Depth First Search (DFS) is the most
fundamental search algorithm used to explore
nodes and edges of a graph. It runs with a
time complexity of O(V+E) and is often used as
a building block in other algorithms.
By itself the DFS isn’t all that useful, but
when augmented to perform other tasks such as
count connected components, determine
connectivity, or find bridges/articulation
points then DFS really shines.
As the name suggests, a DFS plunges depth first
into a graph without regard for which edge it
takes next until it cannot go any further at
which point it backtracks and continues.
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Start DFS at node 0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Pick an edge outwards from node 0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Once at 9 pick an edge outwards from node 9
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Go to node 8
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Pick an edge outwards from 8…
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Make sure you don’t re-visit visited nodes!
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
We haven’t finished visiting all the
neighbours of 7 so continue DFS in another
direction
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Backtrack when a dead end is reached.
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
Basic DFS
7
3
11
10
8
6
9
1
2
5
4
12
0
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
visited = [false, …, false] # size n
function dfs(at):
if visited[at]: return
visited[at] = true
neighbours = graph[at]
for next in neighbours:
dfs(next)
# Start DFS at node zero
start_node = 0
dfs(start_node)
Connected
Components
Connected Components
Sometimes a graph is split into multiple
components. It’s useful to be able to
identify and count these components.
Connected Components
Sometimes a graph is split into multiple
components. It’s useful to be able to
identify and count these components.
Connected Components
Assign an integer value to each group to
be able to tell them apart.
0
0
0
2
1
1 1
1
1
3
3
3
3
3
4
4
4
4
We can use a DFS to identify components.
First, make sure all the nodes are labeled
from [0, n) where n is the number of nodes.
6
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
6
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
7
11
6
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
1
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
1
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
6
7
11
Algorithm: Start a DFS at every node (except if
it’s already been visited) and mark all reachable
nodes as being part of the same component.
0
0
0
0
0
1
1
1
1
… and so on for the other components
6
7
11
1
5
17
16
12
4
0
8 14
13
15
9
10
3
2
0
0
0
0
0
1
1
1
1
2
2
2
3
3
3
3
3
4
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
# Global or class scope variables
n = number of nodes in the graph
g = adjacency list representing graph
count = 0
components = empty integer array # size n
visited = [false, …, false] # size n
function findComponents():
for (i = 0; i < n; i++):
if !visited[i]:
count++
dfs(i)
return (count, components)
function dfs(at):
visited[at] = true
components[at] = count
for (next : g[at]):
if !visited[next]:
dfs(next)
What else can DFS do?
We can augment the DFS algorithm to:
• Compute a graph’s minimum spanning tree.
• Detect and find cycles in a graph.
• Check if a graph is bipartite.
• Find strongly connected components.
• Topologically sort the nodes of a graph.
• Find bridges and articulation points.
• Find augmenting paths in a flow network.
• Generate mazes.
Next Video: Breadth First Search
7
3
11
10
8
6
9
1
2
5
4
12
0
Depth First Search
Algorithm
Graph Theory
Video Series
Graph Theory:
Breadth First Search
William Fiset
BFS overview
The Breadth First Search (BFS) is another
fundamental search algorithm used to explore
nodes and edges of a graph. It runs with a
time complexity of O(V+E) and is often used
as a building block in other algorithms.
The BFS algorithm is particularly useful
for one thing: finding the shortest path on
unweighted graphs.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
12
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
12
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
3
1
5
12
2
4 A BFS starts at some arbitrary node
of a graph and explores the neighbour
nodes first, before moving to the
next level neighbours.
Using a Queue
The BFS algorithm uses a queue data structure
to track which node to visit next. Upon
reaching a new node the algorithm adds it to
the queue to visit it later. The queue data
structure works just like a real world queue
such as a waiting line at a restaurant.
People can either enter the waiting line
(enqueue) or get seated (dequeue).
Queue Front
Queue Back
Dequeue
Enqueue
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
function solve(s):
q = queue data structure with enqueue and dequeue
q.enqueue(s)
visited = [false, …, false] # size n
visited[s] = true
prev = [null, …, null] # size n
while !q.isEmpty():
node = q.dequeue()
neighbours = g.get(node)
for(next : neighbours):
if !visited[next]:
q.enqueue(next)
visited[next] = true
prev[next] = node
return prev
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
# Global/class scope variables
n = number of nodes in the graph
g = adjacency list representing unweighted graph
# s = start node, e = end node, and 0 ≤ e,s < n
function bfs(s, e):
# Do a BFS starting at node s
prev = solve(s)
# Return reconstructed path from s -> e
return reconstructPath(s, e, prev)
function reconstructPath(s, e, prev):
# Reconstruct path going backwards from e
path = []
for(at = e; at != null; at = prev[at]):
path.add(at)
path.reverse()
# If s and e are connected return the path
if path[0] == s:
return path
return []
function reconstructPath(s, e, prev):
# Reconstruct path going backwards from e
path = []
for(at = e; at != null; at = prev[at]):
path.add(at)
path.reverse()
# If s and e are connected return the path
if path[0] == s:
return path
return []
function reconstructPath(s, e, prev):
# Reconstruct path going backwards from e
path = []
for(at = e; at != null; at = prev[at]):
path.add(at)
path.reverse()
# If s and e are connected return the path
if path[0] == s:
return path
return []
function reconstructPath(s, e, prev):
# Reconstruct path going backwards from e
path = []
for(at = e; at != null; at = prev[at]):
path.add(at)
path.reverse()
# If s and e are connected return the path
if path[0] == s:
return path
return []
7
3
11
0
8
6
9
1
2
5
4
12
10
0
9
7
11
10
8
6
Breadth First
Search Algorithm
Graph Theory
Video Series
BFS Shortest
Path on a Grid
William Fiset
BFS Video
Please watch Breadth First Search Algorithm to
understand the basics of a BFS before proceeding.
Link should be in the description below.
Motivation
Many problems in graph theory can be represented
using a grid. Grids are a form of implicit graph
because we can determine a node’s neighbours
based on our location within the grid.
Motivation
Many problems in graph theory can be represented
using a grid. Grids are a form of implicit graph
because we can determine a node’s neighbours
based on our location within the grid.
A type of problem that involves finding a path
through a grid is solving a maze:
Motivation
Many problems in graph theory can be represented
using a grid. Grids are a form of implicit graph
because we can determine a node’s neighbours
based on our location within the grid.
Another example could be routing through obstacles
(trees, rivers, rocks, etc) to get to a location:
Graph Theory on Grids
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
Empty Grid
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
First label the cells in the
grid with numbers [0, n)
where n = #rows x #columns
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 1 0 0 1
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
3 -> [1, 2, 5]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 1 0 0 1
0 0 1 0 0 1
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
3 -> [1, 2, 5]
4 -> [2, 5]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 1 0 0 1
0 0 1 0 0 1
0 0 0 1 1 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
3 -> [1, 2, 5]
4 -> [2, 5]
5 -> [3, 4]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
IMPORTANT: Assume the grid is unweighted and
cells connect left, right, up and down.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 1 0 0 1
0 0 1 0 0 1
0 0 0 1 1 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Graph Theory on Grids
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
3 -> [1, 2, 5]
4 -> [2, 5]
5 -> [3, 4]
A common approach to solving graph theory problems
on grids is to first convert the grid to a
familiar format such as an adjacency list/matrix.
However, transformations between graph
representations can usually be avoided
due to the structure of a grid.
Once we have an adjacency list/matrix we
can run whatever specialized graph
algorithm to solve our problem such as:
shortest path, connected components, etc…
Graph Theory on Grids
Direction Vectors
Due to the structure of a grid, if we are at the
red ball in the middle we know we can move left,
right, up and down to reach adjacent cells:
Direction Vectors
Mathematically, if the red ball is at the row-
column coordinate (r, c) we can add the row
vectors [-1, 0], [1, 0], [0, 1], and [0, -1] to
reach adjacent cells.
(r, c)
(r-1, c)
(r+1, c)
(r, c+1)
(r, c-1)
Direction Vectors
If the problem you are trying to solve allows
moving diagonally then you can also include the
row vectors: [-1, -1], [-1, 1], [1, 1], [1, -1]
(r, c)
(r-1, c)
(r+1, c)
(r, c+1)
(r, c-1)
(r-1, c+1)
(r+1, c+1)
(r+1, c-1)
(r-1, c-1)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Direction Vectors
This makes it very easy to access neighbouring
cells from the current row-column position:
# Define the direction vectors for
# north, south, east and west.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip invalid cells. Assume R and
# C for the number of rows and columns
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
#(rr, cc) is a neighbouring cell of (r, c)
Dungeon Problem Statement
You are trapped in a 2D dungeon and need to
find the quickest way out! The dungeon is
composed of unit cubes which may or may not
be filled with rock. It takes one minute to
move one unit north, south, east, west. You
cannot move diagonally and the maze is
surrounded by solid rock on all sides.
Is an escape possible?
If yes, how long will
it take?
This is an easier version of the “Dungeon Master” problem on
Kattis: open.kattis.com/problems/dungeon. Link in description.
Dungeon Problem Statement
The dungeon has a size of R x C and you start
at cell ’S’ and there’s an exit at cell ‘E’.
A cell full of rock is indicated by a ’#’ and
empty cells are represented by a ’.’
R
C
S . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
Dungeon Problem Statement
S . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
R
C
The dungeon has a size of R x C and you start
at cell ’S’ and there’s an exit at cell ‘E’.
A cell full of rock is indicated by a ’#’ and
empty cells are represented by a ’.’
S . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 0)
(0,0) . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
Start at the start node coordinate by
adding (sr, sc) to the queue.
(0,0) (0,1) . # . . .
(1,0) # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 0)
(0,0) (0,1) (0,2) # . . .
(1,0) # . . . # .
(2,0) # . . . . .
. . # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(0,0) (0,1) (0,2) # . . .
(1,0) # (1,2) . . # .
(2,0) # . . . . .
(3,0) . # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(0,0) (0,1) (0,2) # . . .
(1,0) # (1,2) (1,3) . # .
(2,0) # (2,2) . . . .
(3,0) (3,1) # # . . .
# . # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(0,0) (0,1) (0,2) # . . .
(1,0) # (1,2) (1,3) (1,4) # .
(2,0) # (2,2) (2,3) . . .
(3,0) (3,1) # # . . .
# (4,1) # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(0,0) (0,1) (0,2) # (0,4) . .
(1,0) # (1,2) (1,3) (1,4) # .
(2,0) # (2,2) (2,3) (2,4) . .
(3,0) (3,1) # # . . .
# (4,1) # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(2, 4)
(0, 4)
(0,0) (0,1) (0,2) # (0,4) (0,5) .
(1,0) # (1,2) (1,3) (1,4) # .
(2,0) # (2,2) (2,3) (2,4) (2,5) .
(3,0) (3,1) # # (3,4) . .
# (4,1) # E . # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(2, 4)
(0, 4)
2, 5)
(3, 4)
(0, 5)
(0,0) (0,1) (0,2) # (0,4) (0,5) (0,6)
(1,0) # (1,2) (1,3) (1,4) # .
(2,0) # (2,2) (2,3) (2,4) (2,5) (2,6)
(3,0) (3,1) # # (3,4) (3,5) .
# (4,1) # E (4,4) # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(2, 4)
(0, 4)
2, 5)
(3, 4)
(0, 5)
(3, 5)
(4, 4)
(0, 6)
(2, 6)
(0,0) (0,1) (0,2) # (0,4) (0,5) (0,6)
(1,0) # (1,2) (1,3) (1,4) # (1,6)
(2,0) # (2,2) (2,3) (2,4) (2,5) (2,6)
(3,0) (3,1) # # (3,4) (3,5) (3,6)
# (4,1) # (4,3) (4,4) # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(2, 4)
(0, 4)
2, 5)
(3, 4)
(0, 5)
(3, 5)
(4, 4)
(0, 6)
(2, 6)
(3,6)
(1, 6)
(4, 3)
(0,0) (0,1) (0,2) # (0,4) (0,5) (0,6)
(1,0) # (1,2) (1,3) (1,4) # (1,6)
(2,0) # (2,2) (2,3) (2,4) (2,5) (2,6)
(3,0) (3,1) # # (3,4) (3,5) (3,6)
# (4,1) # (4,3) (4,4) # .
0 1 2 3 4 5 6
0
1
2
3
4
(0, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 0)
(1, 2)
(3, 0)
(1, 3)
(2, 2)
(3, 1)
(1, 4)
(2, 3)
(4, 1)
(2, 4)
(0, 4)
2, 5)
(3, 4)
(0, 5)
(3, 5)
(4, 4)
(0, 6)
(2, 6)
(3,6)
(1, 6)
(4, 3)
We have reached the end, and if we had
a 2D prev matrix we could regenerate
the path by retracing our steps.
Alternative State representation
So far we have been storing the next x-y
position in the queue as an (x, y) pair.
This works well but requires either an array
or an object wrapper to store the coordinate
values. In practice, this requires a lot of
packing and unpacking of values to and from
the queue.
Let’s take a look at an alternative approach
which also scales well in higher dimensions
and (IMHO) requires less setup effort…
Alternative State representation
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
Alternative State representation
x1 y1 z1
Enqueuing (x1, y1, z1)
x queue y queue z queue
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
Alternative State representation
x1 y1 z1
x queue y queue z queue
x2 y2 z2
Enqueuing (x2, y2, z2)
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
Alternative State representation
x1 y1 z1
x queue y queue z queue
x2 y2 z2
Enqueuing (x3, y3, z3)
x3 y3 z3
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
Alternative State representation
x1 y1 z1
x queue y queue z queue
x2 y2 z2
Dequeuing (x3, y3, z3)
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
x3 y3 z3
Alternative State representation
x1 y1 z1
x queue y queue z queue
Dequeuing (x2, y2, z2)
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
x2 y2 z2
Alternative State representation
x1 y1 z1
x queue y queue z queue
An alternative approach is to use one queue
for each dimension, so in a 3D grid you
would have one queue for each of the x, y,
and z dimensions.
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
# Global/class scope variables
R, C = … # R = number of rows, C = number of columns
m = … # Input character matrix of size R x C
sr, sc = … # ’S’ symbol row and column values
rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ)
# Variables used to track the number of steps taken.
move_count = 0
nodes_left_in_layer = 1
nodes_in_next_layer = 0
# Variable used to track whether the ‘E’ character
# ever gets reached during the BFS.
reached_end = false
# R x C matrix of false values used to track whether
# the node at position (i, j) has been visited.
visited = …
# North, south, east, west direction vectors.
dr = [-1, +1, 0, 0]
dc = [ 0, 0, +1, -1]
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function explore_neighbours(r, c):
for(i = 0; i < 4; i++):
rr = r + dr[i]
cc = c + dc[i]
# Skip out of bounds locations
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
# Skip visited locations or blocked cells
if visited[rr][cc]: continue
if m[rr][cc] == ‘#’: continue
rq.enqueue(rr)
cq.enqueue(cc)
visited[rr][cc] = true
nodes_in_next_layer++
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
function solve():
rq.enqueue(sr)
cq.enqueue(sc)
visited[sr][sc] = true
while rq.size() > 0: # or cq.size() > 0
r = rq.dequeue()
c = cq.dequeue()
if m[r][c] == ‘E’:
reached_end = true
break
explore_neighbours(r, c)
nodes_left_in_layer—-
if nodes_left_in_layer == 0:
nodes_left_in_layer = nodes_in_next_layer
nodes_in_next_layer = 0
move_count++
if reached_end:
return move_count
return -1
Summary
Representing a grid as an adjacency list and
adjacency matrix.
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 1 1 0
0 1 1 0 0 1
0 0 1 0 0 1
0 0 0 1 1 0
0 1 2 3 4 5
0
1
2
3
4
5
0 1
2 3
4 5
Empty Grid
Adjacency Matrix:
Adjacency List:
0 -> [1, 2]
1 -> [0, 3]
2 -> [0, 3, 4]
3 -> [1, 2, 5]
4 -> [2, 5]
5 -> [3, 4]
Summary
Using direction vectors to visit
neighbouring cells.
(r, c)
(r-1, c)
(r+1, c)
(r, c+1)
(r, c-1)
Summary
Explored an alternative way to represent multi
dimensional coordinates using multiple queues.
x1 y1 z1
x queue y queue z queue
x2 y2 z2
Dequeuing (x3, y3, z3)
x3 y3 z3
Summary
How to use BFS on a grid to find the
shortest path between two cells.
S . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
R
C
S . . # . . .
. # . . . # .
. # . . . . .
. . # # . . .
# . # E . # .
Breadth First Search
Shortest Path on a Grid
Graph Theory
Video Series
Topological
Sort
William Fiset
Many real world situations can be modelled
as a graph with directed edges where some
events must occur before others.
• School class prerequisites
• Program dependencies
• Event scheduling
• Assembly instructions
• Etc…
Class B
Class E
Class F
Class A
Class C
Class D
Class H
Class I
Class J
Suppose you’re a student at university X and
you want to take Class H, then you must take
classes A, B, D and E as prerequisites.
In this sense there is an ordering on the
nodes of the graph.
Class B
Class E
Class F
Class A
Class C
Class D
Class H
Class I
Class J
Suppose you’re a student at university X and
you want to take Class H, then you must take
classes A, B, D and E as prerequisites.
In this sense there is an ordering on the
nodes of the graph.
Class B
Class E
Class F
Class A
Class C
Class D
Class H
Class I
Class J
Suppose you’re a student at university X and
you want to take Class H, then you must take
classes A, B, D and E as prerequisites.
In this sense there is an ordering on the
nodes of the graph.
Class B
Class E
Class F
Class A
Class C
Class D
Class H
Class I
Class J
Suppose you’re a student at university X and
you want to take Class H, then you must take
classes A, B, D and E as prerequisites.
In this sense there is an ordering on the
nodes of the graph.
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Want to build
program J
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
A
B
C
D
E
F
G
H
I
J
K
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
H
I
J
K
A
B
C
D
E
F
G
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
H
I
J
K
A
B
C
D
E
F
G
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
H
I
J
K
A
B
C
D
E
F
G
Not a dependency
Another canonical example where an ordering
on the nodes of the graph matters is for
program build dependencies. A program cannot
be built unless its dependencies are first
built.
H
I J
K
A C B D F E G
A topological ordering is an ordering of the
nodes in a directed graph where for each
directed edge from node A to node B, node A
appears before node B in the ordering.
NOTE: Topological orderings are NOT unique.
The topological sort algorithm can find a
topological ordering in O(V+E) time!
Directed Acyclic Graphs(DAG)
Not every graph can have a topological
ordering. A graph which contains a cycle
cannot have a valid ordering:
1
0
2
5
4
3
Directed Acyclic Graphs(DAG)
1
0
2
5
4
3
Not every graph can have a topological
ordering. A graph which contains a cycle
cannot have a valid ordering:
Directed Acyclic Graphs(DAG)
The only type of graph which has a valid
topological ordering is a Directed Acyclic
Graph (DAG). These are graphs with directed
edges and no cycles.
Directed Acyclic Graphs(DAG)
Q: How do I verify that my graph does not
contain a directed cycle?
A: One method is to use Tarjan’s strongly
connected component algorithm which can be
used to find these cycles.
The only type of graph which has a valid
topological ordering is a Directed Acyclic
Graph (DAG). These are graphs with directed
edges and no cycles.
A
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
E
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
E
D
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
E
D
C
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
E
D
C
A
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
A
Directed Acyclic Graphs(DAG)
B C
D
G
F
I
E
H
J
K
I
F
J
K
H
G
B
E
D
C
A
Topological ordering from left to right:
By definition, all rooted trees have a
topological ordering since they do not contain
any cycles.
Pick an unvisited node
Beginning with the selected node, do a
Depth First Search (DFS) exploring only
unvisited nodes.
On the recursive callback of the DFS, add
the current node to the topological
ordering in reverse order.
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Topological Sort Algorithm
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Node H
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Node H
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Node H
Node J
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Node H
Node J
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _
Node H
Node J
Node M
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ M
Node H
Node J
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ M
Node H
Node J
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ M
Node H
Node J
Node L
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ L M
Node H
Node J
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ J L M
Node H
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ J L M
Node H
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ J L M
Node H
Node I
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ J L M
Node H
Node I
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ J L M
Node H
Node I
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ I J L M
Node H
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Node G
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Node G
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ H I J L M
Node E
Node A
Node D
Node G
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ G H I J L M
Node E
Node A
Node D
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ D G H I J L M
Node E
Node A
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Node F
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Node F
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Node F
Node K
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Node F
Node K
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ A D G H I J L M
Node E
Node F
Node K
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ K A D G H I J L M
Node E
Node F
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ K A D G H I J L M
Node E
Node F
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ K A D G H I J L M
Node E
Node F
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ F K A D G H I J L M
Node E
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Node C
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Node C
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Node C
Node B
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Node C
Node B
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ E F K A D G H I J L M
Node C
Node B
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
_ B E F K A D G H I J L M
Node C
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
B E F K A D G H I J L M
Node C
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
B E F K A D G H I J L M
Node C
Topological Sort Algorithm
A
I
E
C
F
B
D
G
J
H
K
L
M
DFS recursion
call stack:
Topological ordering:
_ _ _ _ _ _ _ _ _ _ _ _ _
C B E F K A D G H I J L M
Topological Sort Algorithm
# Assumption: graph is stored as adjacency list
function topsort(graph):
N = graph.numberOfNodes()
V = [false,…,false] # Length N
ordering = [0,…,0] # Length N
i = N - 1 # Index for ordering array
for(at = 0; at < N; at++):
if V[at] == false:
visitedNodes = []
dfs(at, V, visitedNodes, graph)
for nodeId in visitedNodes:
ordering[i] = nodeId
i = i - 1
return ordering
Topsort pseudocode
# Execute Depth First Search (DFS)
function dfs(at, V, visitedNodes, graph):
V[at] = true
edges = graph.getEdgesOutFromNode(at)
for edge in edges:
if V[edge.to] == false:
dfs(edge.to, V, visitedNodes, graph)
visitedNodes.add(at)
Topsort pseudocode
# Assumption: graph is stored as adjacency list
function topsort(graph):
N = graph.numberOfNodes()
V = [false,…,false] # Length N
ordering = [0,…,0] # Length N
i = N - 1 # Index for ordering array
for(at = 0; at < N; at++):
if V[at] == false:
visitedNodes = []
dfs(at, V, visitedNodes, graph)
for nodeId in visitedNodes:
ordering[i] = nodeId
i = i - 1
return ordering
Topsort pseudocode
# Assumption: graph is stored as adjacency list
function topsort(graph):
N = graph.numberOfNodes()
V = [false,…,false] # Length N
ordering = [0,…,0] # Length N
i = N - 1 # Index for ordering array
for(at = 0; at < N; at++):
if V[at] == false:
visitedNodes = []
dfs(at, V, visitedNodes, graph)
for nodeId in visitedNodes:
ordering[i] = nodeId
i = i - 1
return ordering
Topsort pseudocode
# Assumption: graph is stored as adjacency list
function topsort(graph):
N = graph.numberOfNodes()
V = [false,…,false] # Length N
ordering = [0,…,0] # Length N
i = N - 1 # Index for ordering array
for(at = 0; at < N; at++):
if V[at] == false:
i = dfs(i, at, V, ordering, graph)
return ordering
Topsort Optimization
# Execute Depth First Search (DFS)
function dfs(i, at, V, ordering, graph):
V[at] = true
edges = graph.getEdgesOutFromNode(at)
for edge in edges:
if V[edge.to] == false:
i = dfs(i, edge.to, V, ordering, graph)
ordering[i] = at
return i - 1
Topsort Optimization
Source Code Link
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description:
A
I
E
C
F
B
D
G
J
H
K
L
M
Topological Sort
Algorithm
Graph Theory
Video Series
Kahn’s
Algorithm
William Fiset
An intuitive topological sort algorithm
e
c
b
a
f
d
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
How to get dressed
school
shoes
socks
underwear
pants
shirt hoodie
One (of many) possible Topological Orderings:
Many real world situations can be modeled as a
graph with directed edges where some events
must occur before others, for instance:
• Program build dependencies
• College class prerequisites
• Event scheduling
• Assembly instructions
• Etc…
Motivation
H
I J
K
A C B D F E G
A topological ordering is an ordering of the
nodes in a directed graph where for each
directed edge from node A to node B, node A
appears before node B in the ordering.
NOTE: Topological orderings are NOT unique.
Kahn’s algorithm is a simple topological
sort algorithm can find a topological
ordering in O(V+E) time!
Which graphs have topological sorts?
Only certain types of graphs have a
topological orderings. These are Directed
Acyclic Graphs (DAGs).
"A directed acyclic graph is a finite
directed graph with no directed cycles."
- Wiki
Which of these are Directed Acyclic Graphs (DAGs)?
(1) (2) (3)
(4) (5) (6)
Which of these are Directed Acyclic Graphs (DAGs)?
Edges must be
directed
(1) (2) (3)
(4) (5) (6)
Which graphs have topological sorts?
Try and imagine what happens if you try
and find the topological ordering of a
graph that contains a cycle:
4
1
0
5
3
2
Which graphs have topological sorts?
First you would pick node 4, the only
node without any dependencies
4
1
0
5
3
2
4,
Which graphs have topological sorts?
Then, you could process node 1, which no
longer depends on node 4
4
1
0
5
3
2
4, 1
Which graphs have topological sorts?
Followed by node 0, which no longer
depends on node 1
4
1
0
5
3
2
4, 1, 0
Which graphs have topological sorts?
Then you get stuck in a cycle. Every
node depends on another node 😱
4
1
0
5
3
2
4, 1, 0, ???
Kahn’s Algorithm Intuition
The intuition behind Kahn’s algorithm is to
repeatedly remove nodes without any
dependencies from the graph and add them to
the topological ordering.
As nodes without dependencies (and their
outgoing edges) are removed from the
graph, new nodes without dependencies
should become free.
We repeat removing nodes without
dependencies from the graph until all nodes
are processed, or a cycle is discovered.
0
5
1
3
4
Topological Ordering:
_ _ _ _ _ _
2
0
5
1
3
4
Topological Ordering:
_ _ _ _ _ _
2
Node 2 is the only node without dependencies
0
5
1
3
4
Topological Ordering:
2 _ _ _ _ _
2
Add node 2 to the topological ordering, and
remove it from the graph.
0
5
1
3
4
Topological Ordering:
2 _ _ _ _ _
2
Node 0 and node 4 don’t have any
dependencies. Select either to be added
to the topological ordering.
0
5
1
3
4
Topological Ordering:
2 0 _ _ _ _
2
Add node 0 to the topological ordering, and
remove it from the graph.
0
5
1
3
4
Topological Ordering:
2 0 _ _ _ _
2
Node 4 is the only node without dependencies
0
5
1
3
4
Topological Ordering:
2 0 4 _ _ _
2
Add node 4 to the topological ordering, and
remove it from the graph.
0
5
1
3
4
Topological Ordering:
2 0 4 _ _ _
2
Node 3 and node 5 don’t have any
dependencies. Select either to be added
to the topological ordering.
0
5
1
3
4
Topological Ordering:
2 0 4 3 _ _
2
Add node 3 to the topological ordering, and
remove it from the graph.
0
5
1
3
4
Topological Ordering:
2 0 4 3 _ _
2
Node 5 is the only node without dependencies
0
5
1
3
4
Topological Ordering:
2 0 4 3 5 _
2
Add node 5 to the topological ordering, and
remove it from the graph.
0
5
1
3
4
Topological Ordering:
2 0 4 3 5 _
2
Finally, only node 1 remains
0
5
1
3
4
Topological Ordering:
2 0 4 3 5 1
2
2
12
0
9
3
10
6
11
4
7
1
8
5
13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
_ _ _ _ _ _ _ _ _ _ _ _ _ _
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
_ _ _ _ _ _ _ _ _ _ _ _ _ _
Begin by counting the incoming degree of each node
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
3
_ _ _ _ _ _ _ _ _ _ _ _ _ _
For example, node 6 has an incoming degree of 3
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
3 1
_ _ _ _ _ _ _ _ _ _ _ _ _ _
And node 7 has an incoming degree of 1
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 1 3 1 3 1 2 0 1 1 2 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _
Nodes by incoming degree count:
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 1 3 1 3 1 2 0 1 1 2 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _
Maintain a queue of all nodes with no incoming edges
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 0
node 9
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 1 3 1 3 1 2 0 1 1 2 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _
Add all nodes with a degree of 0 (no incoming edges)
to the queue. These are all the nodes in the graph
with no dependencies.
Topological Ordering:
2
12
9
3
10
6
11
4
7
1
8
5
13
Queue
node 0
node 9
node 13
0 _ _ _ _ _ _ _ _ _ _ _ _ _
Remove node 0 from the front of the queue, and add
it to the topological ordering.
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 1 3 1 3 1 2 0 1 1 2 0
0
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 9
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 1 0 3 1 2 1 2 0 1 1 2 0
0 _ _ _ _ _ _ _ _ _ _ _ _ _
Remove node 0 from the graph and decrease the degree
of all affected nodes.
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 9
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 1 0 3 1 2 1 2 0 1 1 2 0
0 _ _ _ _ _ _ _ _ _ _ _ _ _
Add any new nodes with an incoming degree of 0 to
the queue.
node 3
Topological Ordering:
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 9
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 1 0 3 1 2 1 2 0 1 1 2 0
Topological Ordering:
0 9 _ _ _ _ _ _ _ _ _ _ _ _
node 3
Remove node 9 from the front of the queue, and add
it to the topological ordering.
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 0 0 3 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 _ _ _ _ _ _ _ _ _ _ _ _
node 3
Remove node 9 from the graph and decrease the degree
of all affected nodes.
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 0 0 3 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 _ _ _ _ _ _ _ _ _ _ _ _
node 3
Add any new nodes with an incoming degree of 0 to
the queue.
node 2
node 10
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
node 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 0 0 3 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 _ _ _ _ _ _ _ _ _ _ _
node 3
node 2
node 10
Remove node 13 from the front of the queue, and add
it to the topological ordering.
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 0 0 3 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 _ _ _ _ _ _ _ _ _ _ _
node 3
node 2
node 10
Remove node 13 from the graph and decrease the degree
of all affected nodes. Node 13 has no outgoing edges,
so no node degrees are updated (and hence no new nodes
are added to the queue either).
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 0 0 3 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 _ _ _ _ _ _ _ _ _ _
node 3
node 2
node 10
Remove node 3 from the front of the queue, and add
it to the topological ordering.
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 _ _ _ _ _ _ _ _ _ _
node 2
node 10
Remove node 3 from the graph and decrease the degree
of all affected nodes.
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 _ _ _ _ _ _ _ _ _ _
node 2
node 10
Add any new nodes with an incoming degree of 0 to
the queue.
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 _ _ _ _ _ _ _ _ _ _
node 2
node 10
Let the animation play…
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 2 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 _ _ _ _ _ _ _ _ _
node 2
node 10
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 1 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 _ _ _ _ _ _ _ _ _
node 10
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 1 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 _ _ _ _ _ _ _ _
node 10
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 0 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 _ _ _ _ _ _ _ _
node 1
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 0 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 _ _ _ _ _ _ _ _
node 1
node 6
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 2 1 0 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 1 _ _ _ _ _ _ _
node 1
node 6
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 1 1 0 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 1 _ _ _ _ _ _ _
node 6
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 1 1 0 1 2 0 0 1 2 0
Topological Ordering:
0 9 13 3 2 10 1 6 _ _ _ _ _ _
node 6
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 1 1 0 0 2 0 0 0 2 0
Topological Ordering:
0 9 13 3 2 10 1 6 _ _ _ _ _ _
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 1 1 0 0 2 0 0 0 2 0
Topological Ordering:
0 9 13 3 2 10 1 6 _ _ _ _ _ _
node 7
node 11
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 1 1 0 0 2 0 0 0 2 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 _ _ _ _ _
node 7
node 11
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 1 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 _ _ _ _ _
node 11
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 1 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 _ _ _ _ _
node 11
node 4
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 1 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 _ _ _ _
node 11
node 4
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 _ _ _ _
node 4
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 _ _ _ _
node 4
node 12
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 1 0 0 2 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 _ _ _
node 4
node 12
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 1 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 _ _ _
node 12
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 1 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 _ _ _
node 12
node 5
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 1 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 _ _
node 12
node 5
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 _ _
node 5
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 _ _
node 5
node 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 _
node 5
node 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 _
node 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
node 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Queue
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0 0 0 0 0 0 0 0 0 0 0 0 0
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Ordering:
0 9 13 3 2 10 1 6 7 11 4 12 5 8
2
12
0
9
3
10
6
11
4
7
1
8
5
13
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
# `g` is a directed acyclic graph represented as an adjacency list.
function FindTopologicalOrdering(g):
n = g.size()
in_degree = [0,0,…,0,0] # size n
for (i = 0; i < n; i++):
for (to in g[i]):
in_degree[to] = in_degree[to] + 1
# `q` always contains the set nodes with no incoming edges.
q = … # empty integer queue data structure
for (i = 0; i < n; i++):
if (in_degree[i] == 0):
q.enqueue(i)
index = 0
order = [0,0,…0,0] # size n
while (!q.isEmpty()):
at = q.dequeue()
order[index++] = at
for (to in g[at]):
in_degree[to] = in_degree[to] - 1
if in_degree[to] == 0:
q.enqueue(to)
if index != n:
return null # Oops, graph contains a cycle
return order
Source Code Link
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description
2
12
0
9
3
10
6
11
4
7
1
8
5
13
Topological Sort
Kahn’s Algorithm
Graph Theory
Video Series
Shortest and
longest paths on
DAGs
William Fiset
Directed Acyclic Graph (DAG)
Recall that a Directed Acyclic Graph (DAG) is a
graph with directed edges and no cycles. By
definition this means all trees are automatically
DAGs since they do not contain cycles.
Directed Acyclic Graph (DAG)
Recall that a Directed Acyclic Graph (DAG) is a
graph with directed edges and no cycles. By
definition this means all trees are automatically
DAGs since they do not contain cycles.
Q: Is this graph a DAG?
Directed Acyclic Graph (DAG)
Recall that a Directed Acyclic Graph (DAG) is a
graph with directed edges and no cycles. By
definition this means all trees are automatically
DAGs since they do not contain cycles.
Q: Is this graph a DAG?
A: Yes!
Directed Acyclic Graph (DAG)
Recall that a Directed Acyclic Graph (DAG) is a
graph with directed edges and no cycles. By
definition this means all trees are automatically
DAGs since they do not contain cycles.
Q: Is this graph a DAG?
Directed Acyclic Graph (DAG)
Recall that a Directed Acyclic Graph (DAG) is a
graph with directed edges and no cycles. By
definition this means all trees are automatically
DAGs since they do not contain cycles.
Q: Is this graph a DAG?
A: No, the structure may
be a tree, but it does not
have directed edges.
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
11
11
4
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 ∞ ∞ ∞ ∞ ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 ∞ ∞ ∞ ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 ∞ ∞ ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 ∞ ∞ ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 ∞ 14 ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ ∞ ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ 17 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 ∞ 17 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 12 17 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 14 12 9 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 ∞
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
SSSP on DAG
The Single Source Shortest Path (SSSP) problem
can be solved efficiently on a DAG in O(V+E)
time. This is due to the fact that the nodes
can be ordered in a topological ordering via
topsort and processed sequentially.
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
0 3 6 7 3 12 9 11
A B C D E F G H
11
11
4
Arbitrary topological order: A, B, C, D, G, E, F, H
Longest path on DAG
What about the longest path? On a general
graph this problem is NP-Hard, but on a DAG
this problem is solvable in O(V+E)!
The trick is to multiply all edge values by
-1 then find the shortest path and then
multiply the edge values by -1 again!
Longest path on DAG
What about the longest path? On a general
graph this problem is NP-Hard, but on a DAG
this problem is solvable in O(V+E)!
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
11
11
4
The trick is to multiply all edge values by
-1 then find the shortest path and then
multiply the edge values by -1 again!
Longest path on DAG
What about the longest path? On a general
graph this problem is NP-Hard, but on a DAG
this problem is solvable in O(V+E)!
A
B
C
D
E
F
G
H
-3
-6 -8
-4 4 -9
-5 -1
-2 -2
-11
-11
-4
The trick is to multiply all edge values by
-1 then find the shortest path and then
multiply the edge values by -1 again!
Longest path on DAG
What about the longest path? On a general
graph this problem is NP-Hard, but on a DAG
this problem is solvable in O(V+E)!
A
B
C
D
E
F
G
H
-3
-6 -8
-4 4 -9
-5 -1
-2 -2
-11
-11
-4
The trick is to multiply all edge values by
-1 then find the shortest path and then
multiply the edge values by -1 again!
(-3 + -11 + -9) * -1 = 23
Longest path on DAG
What about the longest path? On a general
graph this problem is NP-Hard, but on a DAG
this problem is solvable in O(V+E)!
Source Code Link
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description:
Shortest & Longest
path on a DAG
A
B
C
D
E
F
G
H
3
6 8
4 -4 9
5 1
2 2
11
11
4
Graph Theory
Video Series
Dijkstra’s
Shortest Path
Algorithm
William Fiset
What is Dijkstra’s algorithm?
Dijkstra’s algorithm is a Single Source
Shortest Path (SSSP) algorithm for
graphs with non-negative edge weights.
Depending on how the algorithm is implemented
and what data structures are used the time
complexity is typically O(E*log(V)) which is
competitive against other shortest path
algorithms.
One constraint for Dijkstra’s algorithm is
that the graph must only contain non-negative
edge weights. This constraint is imposed to
ensure that once a node has been visited its
optimal distance cannot be improved.
Algorithm prerequisites
This is property is especially important
because it enables Dijkstra’s algorithm to
act in a greedy manner by always selecting
the next most promising node.
• Lazy Dijkstra’s animation
• Lazy Dijkstra’s pseudo-code
• Finding SP + stopping early optimization
• Using indexed priority queue + decreaseKey
to reduce space and increase performance.
• Eager Dijkstra’s animation
• Eager Dijkstra’s pseudo-code
• Heap optimization with D-ary heap
Outline
The goal of this slide deck is for you to
understand how to implement Dijkstra’s
algorithm and implement it efficiently.
Maintain a ‘dist’ array where the distance to
every node is positive infinity. Mark the
distance to the start node ’s' to be 0.
Maintain a PQ of key-value pairs of (node
index, distance) pairs which tell you which
node to visit next based on sorted min value.
Insert (s, 0) into the PQ and loop while PQ is
not empty pulling out the next most promising
(node index, distance) pair.
Iterate over all edges outwards from the
current node and relax each edge appending a
new (node index, distance) key-value pair to
the PQ for every relaxation.
Quick Algorithm Overview
0
1
2
3 4
1
2 1
5
3
4
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
∞ ∞ ∞ ∞ ∞
0 1 2 3 4
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
∞ ∞ ∞ ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 ∞ ∞ ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 ∞ ∞ ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 4 ∞ ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
Best distance from node 0 to node 1 is:
dist[0] + edge.cost = 0 + 4 = 4
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 4 1 ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
Best distance from node 0 to node 2 is:
dist[0] + edge.cost = 0 + 1 = 1
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 4 1 ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 4 1 ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 ∞ ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
Best distance from node 2 to node 1 is:
dist[2] + edge.cost = 1 + 2 = 3
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 6 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
Best distance from node 2 to node 3 is:
dist[2] + edge.cost = 1 + 5 = 6
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 6 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 6 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
Best distance from node 1 to node 3 is:
dist[1] + edge.cost = 3 + 1 = 4
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
We have already found a better route to get to
node 1 (since dist[1] has value 3) so we can
ignore this entry in the PQ.
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 ∞
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
Best distance from node 3 to node 4 is:
dist[3] + edge.cost = 4 + 3 = 7
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
We have already found a better route to get to
node 3 (since dist[3] has value 4) so we can
ignore this entry in the PQ.
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
Lazy Dijkstra’s
0
1
2
3 4
1
2 1
5
3
4
dist
0 3 1 4 7
0 1 2 3 4
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
(1, 3)
(3, 6)
(3, 4)
(4, 7)
Lazy Dijkstra’s
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
Assume PQ stores (node index, best distance) pairs
sorted by minimum distance.
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
In practice most standard libraries do not support the
decrease key operation for PQs. A way to get around
this is to add a new (node index, best distance) pair
every time we update the distance to a node.
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
As a result, it is possible to have duplicate node
indices in the PQ. This is not ideal, but inserting a
new key-value pair in O(log(n)) is much faster than
searching for the key in the PQ which takes O(n)
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return dist
A neat optimization we can do which ignores stale (index,
dist) pairs in our PQ is to skip nodes where we already
found a better path routing through others nodes before we
got to processing this node.
Finding the optimal path
s
e
If you wish to not only find the optimal
distance to a particular node but also what
sequence of nodes were taken to get there you
need to track some additional information.
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s and
# the prev array to reconstruct the shortest path itself
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
prev = [null, null, …, null] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
prev[edge.to] = index
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
return (dist, prev)
# Finds the shortest path between two nodes.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
# e - the index of the end node (0 ≤ e < n)
function findShortestPath(g, n, s, e):
dist, prev = dijkstra(g, n, s)
path = []
if (dist[e] == ∞) return path
for (at = e; at != null; at = prev[at])
path.add(at)
path.reverse()
return path
Stopping Early
Q: Suppose you know the destination node
you’re trying to reach is ‘e’ and you
start at node ’s’ do you still have to
visit every node in the graph?
s
e
A: Yes, in the worst case. However, it is
possible to stop early once you have
finished visiting the destination node.
Stopping Early
s
e
The main idea for stopping early is that
Dijkstra’s algorithm processes each next most
promising node in order. So if the destination
node has been visited, its shortest distance
will not change as more future nodes are
visited.
Stopping Early
s
e
# Runs Dijkstra’s algorithm and returns the shortest distance
# between nodes ’s’ and ‘e’. If there is no path, ∞ is returned.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
# e - the index of the end node (0 ≤ e < n)
function dijkstra(g, n, s, e):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
pq = empty priority queue
pq.insert((s, 0))
while pq.size() != 0:
index, minValue = pq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
pq.insert((edge.to, newDist))
if index == e:
return dist[e]
return ∞
Eager Dijkstra’s using an
Indexed Priority Queue
Our current lazy implementation of Dijkstra’s
inserts duplicate key-value pairs (keys being
the node index and the value being the shortest
distance to get to that node) in our PQ because
it’s more efficient to insert a new key-value
pair in O(log(n)) than it is to update an
existing key’s value in O(n).
This approach is inefficient for dense graphs
because we end up with several stale outdated
key-value pairs in our PQ. The eager version of
Dijkstra’s avoids duplicate key-value pairs and
supports efficient value updates in O(log(n)) by
using an Indexed Priority Queue (IPQ)
Indexed Priority Queue DS Video
<insert video clip>
github.com/williamfiset/data-structures
0
1
2
3
4
5
5
1
3
1
20
2
3
2
12
3
6
∞ ∞ ∞ ∞ ∞ ∞
dist =
(index, dist)
key-value pairs
0 1 2 3 4 5
Eager Dijkstra’s
0
1
2
3
4
5
5
1
3
1
20
2
3
2
12
3
6
dist =
(index, dist)
key-value pairs
0 1 2 3 4 5
Eager Dijkstra’s
(0, 0)
0 ∞ ∞ ∞ ∞ ∞
0
1
2
3
4
5
5
1
1
2
3
2
3
12
20
3
6
0 ∞ ∞ ∞ ∞ ∞
dist =
(index, dist)
key-value pairs
(0, 0)
0 1 2 3 4 5
Eager Dijkstra’s
0
1
2
3
4
5
5
1
1
2
3
2
3
12
20
3
6
0 5 ∞ ∞ ∞ ∞
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 5)
0 1 2 3 4 5
Best distance from node 0 to node 1:
dist[0] + edge.cost = 0 + 5 = 5
Eager Dijkstra’s
0
1
2
3
4
5
5
1
1
2
3
2
3
12
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 5)
(2, 1)
0 5 1 ∞ ∞ ∞
0 1 2 3 4 5
Best distance from node 0 to node 2:
dist[0] + edge.cost = 0 + 1 = 1
Eager Dijkstra’s
0
1
2
3
4
5
5
1
1
2
3
2
3
12
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 5)
(2, 1)
0 5 1 ∞ ∞ ∞
0 1 2 3 4 5
Eager Dijkstra’s
0
1
2
3
4
5
5
1
1
2
3
2
3
12
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 5)
(2, 1)
0 5 1 ∞ ∞ ∞
0 1 2 3 4 5
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 5)
(2, 1)
0 5 1 ∞ 13 ∞
0 1 2 3 4 5
(4, 13)
Best distance from node 2 to node 4:
dist[2] + edge.cost = 1 + 12 = 13
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 ∞ 13 ∞
0 1 2 3 4 5
(4, 13)
Best distance from node 2 to node 1:
dist[2] + edge.cost = 1 + 3 = 4
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 ∞ 13 ∞
0 1 2 3 4 5
(4, 13)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 ∞ 13 ∞
0 1 2 3 4 5
(4, 13)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 ∞ 13 ∞
0 1 2 3 4 5
(4, 13)
Node 2 has already been visited so we cannot
improve it’s already best distance
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 ∞ 13 ∞
(4, 13)
0 1 2 3 4 5
dist[1] + edge.cost = 4 + 20 = 24 > dist[4] = 13
so we cannot update best distance.
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 13 ∞
(4, 13)
0 1 2 3 4 5
(3, 7)
Best distance from node 1 to node 3:
dist[1] + edge.cost = 4 + 3 = 7
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 13 ∞
(4, 13)
0 1 2 3 4 5
(3, 7)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 13 ∞
(4, 13)
0 1 2 3 4 5
(3, 7)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 13 ∞
(4, 13)
0 1 2 3 4 5
(3, 7)
Node 2 is already visited, therefore we
cannot improve on its best distance
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
3
2
3
20
3
6
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 ∞
(4, 9)
0 1 2 3 4 5
(3, 7)
Best distance from node 3 to node 4:
dist[3] + edge.cost = 7 + 2 = 9
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 13
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 13)
Best distance from node 3 to node 5:
dist[3] + edge.cost = 7 + 6 = 13
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 13
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 13)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 13
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 13)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 10
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 10)
Best distance from node 4 to node 5:
dist[3] + edge.cost = 9 + 1 = 10
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 10
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 10)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 10
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 10)
Eager Dijkstra’s
0
1
2
3
4
5
5
1
12
1
2
6
3
2
3
20
3
dist =
(index, dist)
key-value pairs
(0, 0)
(1, 4)
(2, 1)
0 4 1 7 9 10
(4, 9)
0 1 2 3 4 5
(3, 7)
(5, 10)
Eager Dijkstra’s
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
ipq = empty indexed priority queue
ipq.insert(s, 0)
while ipq.size() != 0:
index, minValue = ipq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
if !ipq.contains(edge.to):
ipq.insert(edge.to, newDist)
else:
ipq.decreaseKey(edge.to, newDist)
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
ipq = empty indexed priority queue
ipq.insert(s, 0)
while ipq.size() != 0:
index, minValue = ipq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
if !ipq.contains(edge.to):
ipq.insert(edge.to, newDist)
else:
ipq.decreaseKey(edge.to, newDist)
return dist
# Runs Dijkstra’s algorithm and returns an array that contains
# the shortest distance to every node from the start node s.
# g - adjacency list of weighted graph
# n - the number of nodes in the graph
# s - the index of the starting node (0 ≤ s < n)
function dijkstra(g, n, s):
vis = [false, false, … , false] # size n
dist = [∞, ∞, … ∞, ∞] # size n
dist[s] = 0
ipq = empty indexed priority queue
ipq.insert(s, 0)
while ipq.size() != 0:
index, minValue = ipq.poll()
vis[index] = true
if dist[index] < minValue: continue
for (edge : g[index]):
if vis[edge.to]: continue
newDist = dist[index] + edge.cost
if newDist < dist[edge.to]:
dist[edge.to] = newDist
if !ipq.contains(edge.to):
ipq.insert(edge.to, newDist)
else:
ipq.decreaseKey(edge.to, newDist)
return dist
The main advantage to using decreaseKey is to prevent
duplicate node indexes to be present in the PQ.
D-ary Heap optimization
When executing Dijkstra’s algorithm,
especially on dense graphs, there are a lot
more updates (i.e decreaseKey operations) to
key-value pairs than there are dequeue
(poll) operations.
A D-ary heap is a heap variant in which
each node has D children. This speeds
up decrease key operations at the
expense of more costly removals.
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,5)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
D-ary Heap (with D = 4)
Suppose we want to update the node with index
6 to have a new shortest distance of 1 (a.k.a
decreaseKey(6, 1))
(5,2)
(3,3)
(6,5)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Suppose we want to update the node with index
6 to have a new shortest distance of 1 (a.k.a
decreaseKey(6, 1))
Assuming we have an Indexed D-ary Heap we can
update the key’s value in O(1) but we still
need to adjust its position.
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Suppose we want to update the node with index
6 to have a new shortest distance of 1 (a.k.a
decreaseKey(6, 1))
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Suppose we want to update the node with index
6 to have a new shortest distance of 1 (a.k.a
decreaseKey(6, 1))
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Suppose we want to update the node with index
6 to have a new shortest distance of 1 (a.k.a
decreaseKey(6, 1))
The whole update took only two operations
because the heap was very flat.
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
(6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3) (6,1)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Value of 2 is the smallest
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Value of 3 is the smallest
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
In a D-ary heap we have to search D children
of the current node to find the minimum
(k, v) pair to swap downwards.
In contrast suppose we want to remove
the root node.
D-ary Heap (with D = 4)
(5,2)
(3,3)
…
…
…
… …
…
…
… …
…
…
(1,7)
(0,7)
(1,9)
(2,6) (8,5) (9,4)
(4,8)
Removals are clearly much more expensive, but
they are also a lot less common in Dijkstra’s
than decreaseKey operations.
Optimal D-ary Heap degree
Q: What is the optimal D-ary heap degree to
maximize performance of Dijkstra’s algorithm?
A: In general D = E/V is the best degree to
use to balance removals against decreaseKey
operations improving Dijkstra’s time
complexity to O(E*logE/V(V)) which is much
better especially for dense graphs which
have lots of decreaseKey operations.
The state of the art
The current state of the art as of now is the
Fibonacci heap which gives Dijkstra’s
algorithm a time complexity of O(E + Vlog(V))
However, in practice, Fibonacci heaps are
very difficult to implement and have a
large enough constant amortized overhead
to make them impractical unless your
graph is quite large.
Source Code and Slides
Implementation source code and slides
can be found at the following link:
github.com/williamfiset/algorithms
Link in the description:
Next Video: Dijkstra source code
Dijkstra’s
Algorithm
source code
William Fiset
Dijkstra’s Shortest
Path Algorithm
S
E
Graph Theory
Video Series
Bellman-Ford
Algorithm
William Fiset
BF algorithm overview
In graph theory, the Bellman-Ford (BF)
algorithm is a Single Source Shortest Path
(SSSP) algorithm. This means it can find the
shortest path from one node to any other
node.
However, BF is not ideal for most SSSP
problems because it has a time complexity of
O(EV). It is better to use Dijkstra’s
algorithm which is much faster. It is on the
order of Θ((E+V)log(V)) when using a binary
heap priority queue.
BF algorithm overview
However, Dijkstra’s algorithm can fail when
the graph has negative edge weights. This
is when BF becomes really handy because it
can be used to detect negative cycles and
determine where they occur.
Finding negative cycles can be useful in
many types of applications. One
particularly neat application arises in
finance when performing an arbitrage
between two or more markets.
Negative Cycles
Negative cycles can manifest themselves in
many ways…
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 1 2
3
4
5
4
-1
3
1 2
3 -2
6
2
2
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
Starting
node
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 1 2
3
4
5
4
-1
3
1 2
3 -2
6
2
2
Starting
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 1 2
3
4
5
4 3
1 2
3 -2
6
2
2
-1
Starting
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0
2
1
3
Starting
node 4
5
3
1
1
1
4
-6
1
1 1
1
1
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0
2
1
3
Starting
node 4
5
3
1
1
1
4
-6
1
1 1
1
1
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0
2
1
3
Starting
node 4
5
3
1
1
1
4
-6
1
1 1
1
1
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
BF Algorithm Steps
Let E be the number of edges.
Let V be the number of vertices.
Let S be the id of the starting node.
Let D be an array of size V that tracks
the best distance from S to each node.
Let’s define a few variables…
BF Algorithm Steps
1) Set every entry in D to +∞
2) Set D[S] = 0
3) Relax each edge V-1 times:
BF Algorithm Steps
1) Set every entry in D to +∞
2) Set D[S] = 0
3) Relax each edge V-1 times:
for (i = 0; i < V-1; i = i + 1):
for edge in graph.edges:
// Relax edge (update D with shorter path)
if (D[edge.from] + edge.cost < D[edge.to])
D[edge.to] = D[edge.from] + edge.cost
BF Algorithm Steps
1) Set every entry in D to +∞
2) Set D[S] = 0
3) Relax each edge V-1 times:
for (i = 0; i < V-1; i = i + 1):
for edge in graph.edges:
// Relax edge (update D with shorter path)
if (D[edge.from] + edge.cost < D[edge.to])
D[edge.to] = D[edge.from] + edge.cost
// Repeat to find nodes caught in a negative cycle
for (i = 0; i < V-1; i = i + 1):
for edge in graph.edges:
if (D[edge.from] + edge.cost < D[edge.to])
D[edge.to] = -∞
0 ∞
1 ∞
2 ∞
3 ∞
4 ∞
5 ∞
6 ∞
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 ∞
2 ∞
3 ∞
4 ∞
5 ∞
6 ∞
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 ∞
3 ∞
4 ∞
5 ∞
6 ∞
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 25
3 ∞
4 ∞
5 ∞
6 ∞
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 25
3 ∞
4 ∞
5 35
6 ∞
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 25
3 ∞
4 ∞
5 35
6 65
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 25
3 35
4 ∞
5 35
6 65
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 25
3 35
4 100
5 35
6 65
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 100
5 35
6 65
7 ∞
8 ∞
9 ∞
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 100
5 35
6 65
7 ∞
8 ∞
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 65
7 ∞
8 ∞
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 ∞
8 ∞
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 ∞
8 85
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 85
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Iteration 1 complete, 8 more to go…
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 35
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 30
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 20
3 30
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 200
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
NOTE: The edges do not need to be chosen in any specific order.
0 0
1 5
2 15
3 30
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Iteration 2 complete, 7 more to go…
Let’s fast-forward to the end…
0 0
1 5
2 -20
3 -5
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
We’re finished with the SSSP part. Now let’s
detect those negative cycles. If we can
relax an edge then there’s a negative cycle.
0 0
1 5
2 -20
3 -5
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -20
3 -5
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -20
3 -5
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -20
3 -5
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -20
3 -∞
4 60
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -20
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 160
0 1
6
2
5
7
4
8
9
3
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
9
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
9
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
9
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
9
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
9
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
9
2 4
3
Unaffected
node
Directly in
negative cycle
Reachable by
negative cycle
0 0
1 5
2 -∞
3 -∞
4 -∞
5 35
6 40
7 -10
8 -20
9 -∞
0 1
6
5
7 8
-15
10
5
-50 -10
50
60
20 25
100
30
5
75
Start
node
Repeat this for another 8 iterations in order to
ensure the cycles fully propagate. In this
example, we happened to detect all cycles on the
first iteration, but this was a coincidence.
9
2 4
3
0
2
1
3
4
5
3
1
1
1
4
-6
1
1
1
1
1
Bellman-Ford Shortest
Path Algorithm
Graph Theory
Video Series
Floyd-Warshall
Algorithm
William Fiset
All Pairs Shortest Path (APSP)
FW algorithm overview
In graph theory, the Floyd-Warshall (FW)
algorithm is an All-Pairs Shortest Path
(APSP) algorithm. This means it can find
the shortest path between all pairs of
nodes.
The time complexity to run FW is O(V³) which
is ideal for graphs no larger than a couple
hundred nodes.
Shortest Path (SP) Algorithms
BFS Dijkstra’s Bellman
Ford
Floyd
Warshall
Complexity O(V+E) O((V+E)logV) O(VE) O(V³)
Recommended
graph size Large
Large/
Medium
Medium/
Small
Small
Good for
APSP?
Only works on
unweighted
graphs
Ok Bad Yes
Can detect
negative cycles? No No Yes Yes
SP on graph with
weighted edges
Incorrect
SP answer
Best
algorithm
Works
Bad in
general
SP on graph with
unweighted
edges
Best
algorithm
Ok Bad
Bad in
general
Reference: Competitive Programming 3, P. 161, Steven & Felix Halim
BFS Dijkstra’s Bellman
Ford
Floyd
Warshall
Complexity O(V+E) O((V+E)logV) O(VE) O(V³)
Recommended
graph size Large
Large/
Medium
Medium/
Small
Small
Good for
APSP?
Only works on
unweighted
graphs
Ok Bad Yes
Can detect
negative cycles? No No Yes Yes
SP on graph with
weighted edges
Incorrect
SP answer
Best
algorithm
Works
Bad in
general
SP on graph with
unweighted
edges
Best
algorithm
Ok Bad
Bad in
general
Shortest Path (SP) Algorithms
Reference: Competitive Programming 3, P. 161, Steven & Felix Halim
Graph setup
With FW, the optimal way to represent our graph
is with a 2D adjacency matrix m where cell
m[i][j] represents the edge weight of going
from node i to node j.
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
Graph setup
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
NOTE: In the graph above, it is assumed that
the distance from a node to itself is zero.
This is why the diagonal is all zeros.
With FW, the optimal way to represent our graph
is with a 2D adjacency matrix m where cell
m[i][j] represents the edge weight of going
from node i to node j.
Graph setup
If there is no edge from node i to node j
then set the edge value for m[i][j] to be
positive infinity.
0 4 1 ∞
∞ 0 6 ∞
4 1 0 2
∞ ∞ ∞ 0
A B C D
A
B
C
D
A
C
B
D
4
1 6
4 1
2
Graph setup
0 4 1 ∞
∞ 0 6 ∞
4 1 0 2
∞ ∞ ∞ 0
A B C D
A
B
C
D
A
C
B
D
4
1 6
4 1
2
IMPORTANT: If your programming language does not
support a special constant for +∞ such that ∞ + ∞ = ∞
and x + ∞ = ∞ then avoid using 231-1 as infinity!
This will cause integer overflow; prefer to use a
large constant such as 107 instead.
If there is no edge from node i to node j
then set the edge value for m[i][j] to be
positive infinity.
The main idea behind the Floyd-Warshall
algorithm is to gradually build up all
intermediate routes between nodes i and j
to find the optimal path.
Suppose our adjacency matrix tells us that
the distance from a to b is: m[a][b] = 11
The main idea behind the Floyd-Warshall
algorithm is to gradually build up all
intermediate routes between nodes i and j
to find the optimal path.
a b
11
The main idea behind the Floyd-Warshall
algorithm is to gradually build up all
intermediate routes between nodes i and j
to find the optimal path.
a b
11
c
5 5
Suppose there exists a third node, c. If
m[a][c] + m[c][b] < m[a][b] then it’s better
to route through c!
The goal of Floyd-Warshall is to eventually
consider going through all possible intermediate
nodes on paths of different lengths.
a b
11
c
5
?
2
2
The goal of Floyd-Warshall is to eventually
consider going through all possible intermediate
nodes on paths of different lengths.
a b
11
? ?
3 2
c
1 1
a b
11
c
5
?
2
2
The goal of Floyd-Warshall is to eventually
consider going through all possible intermediate
nodes on paths of different lengths.
a b
11
c
1
?
3
-2
?
1
?
0
a b
11
c
5
?
2
2
The goal of Floyd-Warshall is to eventually
consider going through all possible intermediate
nodes on paths of different lengths.
a b
11
? ?
3 2
c
1 1
dp[k][i][j] = shortest path from i to j
routing through nodes {0,1,…,k-1,k}
Start with k = 0, then k = 1, then k = 2, …
This gradually builds up the optimal solution
routing through 0, then all optimal solutions
routing through 0 and 1, then all optimal
solutions routing through 0, 1, 2, etc… up
until n-1 which stores to APSP solution.
Let ‘dp’ (short for Dynamic Programming) be a
3D matrix of size n x n x n that acts as a
memo table.
The Memo Table
Specifically dp[n-1] is the 2D matrix
solution we’re after.
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
otherwise:
dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
Reuse the best distance from i to j with
values routing through nodes {0,1,…,k-1}
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
otherwise:
dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
Find the best distance from i to j through
node k reusing best solutions from {0,1,…,k-1}
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
otherwise:
dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
The right side of the min function in
english essentially says: “go from i to k”
and then “go from k to j”
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
otherwise:
dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
k
i j
Visually this looks like:
dp[k-1][i][j]
dp[k-1][i][k] dp[k-1][k][j]
In the beginning the optimal solution from i to j is
simply the distance in the adjacency matrix.
dp[k][i][j] = m[i][j] if k = 0
otherwise:
Currently we’re using O(V³) memory since our memo
table ‘dp’ has one dimension for each of k, i and j.
Notice that we will be looping over k starting
from 0, then 1, 2… and so fourth. The important
thing to note here is that previous result
builds off the last since we need state k-1 to
compute state k. With that being said, it is
possible to compute the solution for k in-place
saving us a dimension of memory and reducing
the space complexity to O(V²)!
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j])
otherwise:
The new recurrence relation is:
dp[i][j] = m[i][j] if k = 0
Currently we’re using O(V³) memory since our memo
table ‘dp’ has one dimension for each of k, i and j.
Notice that we will be looping over k starting
from 0, then 1, 2… and so fourth. The important
thing to note here is that previous result
builds off the last since we need state k-1 to
compute state k. With that being said, it is
possible to compute the solution for k in-place
saving us a dimension of memory and reducing
the space complexity to O(V²)!
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
function setup(m):
dp = empty matrix of size n x n
# Should contain null values by default
next = empty integer matrix of size n x n
# Do a deep copy of the input matrix and setup
# the 'next' matrix for path reconstruction.
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
dp[i][j] = m[i][j]
if m[i][j] != +∞:
next[i][j] = j
function setup(m):
dp = empty matrix of size n x n
# Should contain null values by default
next = empty integer matrix of size n x n
# Do a deep copy of the input matrix and setup
# the 'next' matrix for path reconstruction.
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
dp[i][j] = m[i][j]
if m[i][j] != +∞:
next[i][j] = j
function setup(m):
dp = empty matrix of size n x n
# Should contain null values by default
next = empty integer matrix of size n x n
# Do a deep copy of the input matrix and setup
# the 'next' matrix for path reconstruction.
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
dp[i][j] = m[i][j]
if m[i][j] != +∞:
next[i][j] = j
function setup(m):
dp = empty matrix of size n x n
# Should contain null values by default
next = empty integer matrix of size n x n
# Do a deep copy of the input matrix and setup
# the 'next' matrix for path reconstruction.
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
dp[i][j] = m[i][j]
if m[i][j] != +∞:
next[i][j] = j
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
Negative Cycles
What do we mean by a negative cycle?
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 1 2
3
4
5
4
-1
3
1 2
3 -2
6
2
2
Unaffected
node
Directly in
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 1 2
3
4
5
4
-1
3
1 2
3 -2
6
2
2
Unaffected
node
Directly in
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
0 2
3
4
5
4 3
1 2
3 -2
6
2
2
-1
1
Unaffected
node
Directly in
negative cycle
Negative Cycles
Negative cycles can manifest themselves in
many ways…
Unaffected
node
Directly in
negative cycle
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
3
Negative Cycles
Negative cycles can manifest themselves in
many ways…
Unaffected
node
Directly in
negative cycle
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
3
Negative Cycles
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
Unaffected
node
Directly in
negative cycle
3
The important thing to ask ourselves is does
the optimal path from node i to node j go
through a red node? If so the path is affected
by the negative cycle and is compromised.
function propagateNegativeCycles(dp, n):
# Execute FW APSP algorithm a second time but
# this time if the distance can be improved
# set the optimal distance to be -∞.
# Every edge (i, j) marked with -∞ is either
# part of or reaches into a negative cycle.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = -∞
next[i][j] = -1
# Global/class scope variables
n = size of the adjacency matrix
dp = the memo table that will contain APSP soln
next = matrix used to reconstruct shortest paths
function floydWarshall(m):
setup(m)
# Execute FW all pairs shortest path algorithm.
for(k := 0; k < n; k++):
for(i := 0; i < n; i++):
for(j := 0; j < n; j++):
if(dp[i][k] + dp[k][j] < dp[i][j]:
dp[i][j] = dp[i][k] + dp[k][j]
next[i][j] = next[i][k]
# Detect and propagate negative cycles.
propagateNegativeCycles(dp, n)
# Return APSP matrix
return dp
# Reconstructs the shortest path between nodes
# ’start’ and ‘end’. You must run the
# floydWarshall solver before calling this method.
# Returns null if path if affected by negative cycle.
function reconstructPath(start, end):
path = []
# Check if there exists a path between
# the start and the end node.
if dp[start][end] == +∞: return path
at := start
# Reconstruct path from next matrix
for(;at != end; at = next[at][end]):
if at == -1: return null
path.add(at)
if next[at][end] == -1: return null
path.add(end)
return path
Source Code Link
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description
Next Video: Floyd-Warshall source code
Floyd-Warshall All
Pairs Shortest Path
Unaffected
node
Directly in
negative cycle
0
2
1
3
4
5
1
1
1
4
-6
1
1
1
1
6
3
Graph Theory
Video Series
Algorithm to Find
Bridges and
Articulation Points
William Fiset
What are bridges &
articulation points?
A bridge / cut edge is any edge in a
graph whose removal increases the number
of connected components.
0
1
2
5
3
6
4
8
7
A bridge / cut edge is any edge in a
graph whose removal increases the number
of connected components.
0
1
2
5
3
6
4
8
7
What are bridges &
articulation points?
An articulation point / cut vertex is any
node in a graph whose removal increases
the number of connected components.
0
1
2
5
3
6
4
8
7
What are bridges &
articulation points?
0
1
2
5
3
6
4
8
7
An articulation point / cut vertex is any
node in a graph whose removal increases
the number of connected components.
What are bridges &
articulation points?
Bridges and articulation points are
important in graph theory because they often
hint at weak points, bottlenecks or
vulnerabilities in a graph. Therefore, it’s
important to be able to quickly find/detect
when and where these occur.
Both problems are related so we will develop
an algorithm to find bridges and then modify
it slightly to find articulation points.
What are bridges &
articulation points?
Bridges algorithm
Start at any node and do a Depth First
Search (DFS) traversal labeling nodes with
an increasing id value as you go. Keep track
the id of each node and the smallest low-
link value. During the DFS, bridges will be
found where the id of the node your edge is
coming from is less than the low link value
of the node your edge is going to.
NOTE: The low-link value of a node is
defined as the smallest [lowest] id
reachable from that node when doing a DFS
(including itself).
Undirected edge Directed edge
DFS traversal
0
DFS traversal
Undirected edge Directed edge
0
1
DFS traversal
Undirected edge Directed edge
0
1
2
DFS traversal
Undirected edge Directed edge
0
1
2
DFS traversal
Undirected edge Directed edge
0
1
2
3
DFS traversal
Undirected edge Directed edge
0
1
2
3 4
DFS traversal
Undirected edge Directed edge
0
1
2
5
3 4
DFS traversal
Undirected edge Directed edge
0
1
2
5
3
6
4
DFS traversal
Undirected edge Directed edge
0
1
2
5
3
6
4
7
DFS traversal
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
DFS traversal
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
Initially all low-link values can be
initialized to the node ids.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
1
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
2
3 4
5
6
7
8
The low-link value of node 1 is 0 since
node 0 is reachable from node 1.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
2
3 4
5
6
7
8
The low-link value of node 2 is 0 since
node 0 is reachable from node 2.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
2
3 4
5
6
7
8
The low-link value of node 2 is 0 since
node 0 is reachable from node 2.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
2
3 4
5
6
7
8
The low-link value of node 2 is 0 since
node 0 is reachable from node 2.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
2
3 4
5
6
7
8
The low-link value of node 2 is 0 since
node 0 is reachable from node 2.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
Undirected edge Directed edge
0
0
0
3 4
5
6
7
8
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
The low-link value of node 2 is 0 since
node 0 is reachable from node 2.
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Cannot update low-link values for nodes
3, 4 and 5.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
6
7
8
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
1
2
5
3
6
4
8
7
0
Undirected edge Directed edge
0
0
3 4
5
5
7
8
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
0
1
2
5
3
6
4
8
7
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
Undirected edge Directed edge
0
0
3 4
5
5
5
5
The low-link value of a node is defined as the
smallest [lowest] id reachable from that node
using forward and backward edges.
0
0
1
2
5
3
6
4
8
7
Node 6’s low-link value can be updated to 5
since node 5 is reachable from node 6.
Undirected edge Directed edge
0
0
3 4
5
5
5
5
Now notice that the condition for a directed
edge ‘e’ to have nodes that belong to a bridge
is when the id(e.from) < lowlink(e.to)*
0
0
1
2
5
3
6
4
8
7
Is bridge
since 2 < 3
Is bridge
since 3 < 4
Is bridge
since 2 < 5
* Where e.from is the node the directed edge starts at and
e.to is the node the directed edge ends at.
Undirected edge Directed edge
0
0
3 4
2
2
2
2
0
0
1
2
5
3
6
4
8
7
Is bridge
since 2 < 3
Is bridge
since 3 < 4
* Where e.from is the node the directed edge starts at and
e.to is the node the directed edge ends at.
Now notice that the condition for a directed
edge ‘e’ to have nodes that belong to a bridge
is when the id(e.from) < lowlink(e.to)*
Undirected edge Directed edge
Complexity
What’s the runtime of our algorithm to find
bridges? Right now we’re doing one DFS to
label all the nodes plus V more DFSs to find
all the low-link values, giving us roughly:
O(V(V+E))
Fortunately, we are able do better by
updating the low-link values in one pass for
O(V+E)
id = 0
g = adjacency list with undirected edges
n = size of the graph
# In these arrays index i represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
visited = [false, …, false] # Length n
function findBridges():
bridges = []
# Finds all bridges in the graph across
# various connected components.
for (i = 0; i < n; i = i + 1):
if (!visited[i]):
dfs(i, -1, bridges)
return bridges
id = 0
g = adjacency list with undirected edges
n = size of the graph
# In these arrays index i represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
visited = [false, …, false] # Length n
function findBridges():
bridges = []
# Finds all bridges in the graph across
# various connected components.
for (i = 0; i < n; i = i + 1):
if (!visited[i]):
dfs(i, -1, bridges)
return bridges
# Perform Depth First Search (DFS) to find bridges.
# at = current node, parent = previous node. The
# bridges list is always of even length and indexes
# (2*i, 2*i+1) form a bridge. For example, nodes at
# indexes (0, 1) are a bridge, (2, 3) is another etc...
function dfs(at, parent, bridges):
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(to, at, bridges)
low[at] = min(low[at], low[to])
if (ids[at] < low[to]):
bridges.add(at)
bridges.add(to)
else:
low[at] = min(low[at], ids[to])
# Perform Depth First Search (DFS) to find bridges.
# at = current node, parent = previous node. The
# bridges list is always of even length and indexes
# (2*i, 2*i+1) form a bridge. For example, nodes at
# indexes (0, 1) are a bridge, (2, 3) is another etc...
function dfs(at, parent, bridges):
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(to, at, bridges)
low[at] = min(low[at], low[to])
if (ids[at] < low[to]):
bridges.add(at)
bridges.add(to)
else:
low[at] = min(low[at], ids[to])
# Perform Depth First Search (DFS) to find bridges.
# at = current node, parent = previous node. The
# bridges list is always of even length and indexes
# (2*i, 2*i+1) form a bridge. For example, nodes at
# indexes (0, 1) are a bridge, (2, 3) is another etc...
function dfs(at, parent, bridges):
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(to, at, bridges)
low[at] = min(low[at], low[to])
if (ids[at] < low[to]):
bridges.add(at)
bridges.add(to)
else:
low[at] = min(low[at], ids[to])
Visited Unvisited
Current
Undirected edge Directed edge
0
0
Visited Unvisited
Current
Undirected edge Directed edge
0
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
0
1
Visited Unvisited
Current
Undirected edge Directed edge
0
1
0
1
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
0
1
2
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
0
1
2
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
0
1
0
Visited Unvisited
Current
Undirected edge Directed edge
# Perform Depth First Search (DFS) to find bridges.
# at = current node, parent = previous node. The
# bridges list is always of even length and indexes
# (2*i, 2*i+1) form a bridge. For example, nodes at
# indexes (0, 1) are a bridge, (2, 3) is another etc...
function dfs(at, parent, bridges):
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(to, at, bridges)
low[at] = min(low[at], low[to])
if (ids[at] < low[to]):
bridges.add(at)
bridges.add(to)
else:
low[at] = min(low[at], ids[to])
0
1
2
0
1
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3
0
1
0
3
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3
0
1
0
3
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3 4
0
1
0
3 4
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3 4
0
1
0
3 4
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3 4
0
1
0
3 4
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
3 4
0
1
0
3 4
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3 4
0
1
0
3 4
5
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3 4
0
1
0
3 4
5
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
0
1
0
3 4
5
6
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
0
1
0
3 4
5
6
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
7
0
1
0
3 4
5
6
7
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
7
0
1
0
3 4
5
6
7
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
0
3 4
5
6
7
8
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
0
3 4
5
6
7
8
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
3 4
5
6
7
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
3 4
5
6
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
# Perform Depth First Search (DFS) to find bridges.
# at = current node, parent = previous node. The
# bridges list is always of even length and indexes
# (2*i, 2*i+1) form a bridge. For example, nodes at
# indexes (0, 1) are a bridge, (2, 3) is another etc...
function dfs(at, parent, bridges):
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(to, at, bridges)
low[at] = min(low[at], low[to])
if (ids[at] < low[to]):
bridges.add(at)
bridges.add(to)
else:
low[at] = min(low[at], ids[to])
0
1
2
5
3
6
4
8
7
0
1
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
1
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Visited Unvisited
Current
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Is bridge
since 2 < 5
The condition for a directed edge ‘e’ to have
nodes that belong to a bridge is when the
id(e.from) < lowlink(e.to)
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Is bridge
since 2 < 3
The condition for a directed edge ‘e’ to have
nodes that belong to a bridge is when the
id(e.from) < lowlink(e.to)
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Is bridge
since 3 < 4
The condition for a directed edge ‘e’ to have
nodes that belong to a bridge is when the
id(e.from) < lowlink(e.to)
Undirected edge Directed edge
0
1
2
5
3
6
4
8
7
0
0
3 4
5
5
5
5
0
Is bridge
since 3 < 4
The condition for a directed edge ‘e’ to have
nodes that belong to a bridge is when the
id(e.from) < lowlink(e.to)
Is bridge
since 2 < 3
Is bridge
since 2 < 5
Undirected edge Directed edge
Articulation points
Articulation points are related very closely
to bridges. It won’t take much modification
to the finding bridges algorithm to find
articulation points.
Articulation points
Simple observation about articulation points:
On a connected component with three or more
vertices if an edge (u, v) is a bridge then
either u or v is an articulation point.
Articulation points
Simple observation about articulation points:
On a connected component with three or more
vertices if an edge (u, v) is a bridge then
either u or v is an articulation point.
0 1
2
3
Bridge
Articulation
point
Articulation points
However, this condition alone is not sufficient to
capture all articulation points. There exist cases
where there is an articulation point without a
bridge:
0
1
2
3
4
There are no bridges but node 2 is an
articulation point since its removal would
cause the graph to split into two components.
Articulation points
0
0
Articulation points
0
1
0
1
Articulation points
0
1
2
0
1
2
Articulation points
0
1 3
2
0
1
2
3
Articulation points
0
1 3
4
2
0
1
2
3
4
Articulation points
0
1 3
4
2
5
0
1
2
3
4
5
Articulation points
0
1 3
4
2
5
0
1
2
3
4
0
Articulation points
0
1 3
4
2
5
0
1
2
3
0
0
Articulation points
0
1 3
4
2
5
0
1
2
0
0
0
Articulation points
0
1 3
4
2
5
0
1
0
0
0
0
Articulation points
0
1 3
4
2
5
0
0
0
0
0
0
Articulation points
0
1 3
4
2
5
0
0
0
0
0
0
Articulation points
0
1 3
4
2
5
0
0
0
0
0
0
On the callback, if id(e.from) == lowlink(e.to)
then there was a cycle.
The indication of a cycle back to the
original node implies an articulation point.
Articulation points
The only time id(e.from) == lowlink(e.to) fails
is when the starting node has 0 or 1 outgoing
directed edges. This is because either the node
is a singleton (0 case) or the node in trapped in
a cycle (1 case).
1
0
3
2
0
0
0
0
Start node
Here the condition is met, but the
starting node only has 1 outgoing
edge. Therefore, the start node is
not an articulation point.
Articulation points
The only time id(e.from) == lowlink(e.to) fails
is when the starting node has 0 or 1 outgoing
directed edges. This is because either the node
is a singleton (0 case) or the node in trapped in
a cycle (1 case).
1
0
3
2
0
0
0
0
Start node
However, when there are more than 1
outgoing edges the starting node can
escape the cycle and thus becomes an
articulation point! 4
4
id = 0
g = adjacency list with undirected edges
n = size of the graph
outEdgeCount = 0
# In these arrays index i represents node i
low = [0, 0, … 0, 0] # Length n
ids = [0, 0, … 0, 0] # Length n
visited = [false, …, false] # Length n
isArt = [false, …, false] # Length n
function findArtPoints():
for (i = 0; i < n; i = i + 1):
if (!visited[i]):
outEdgeCount = 0 # Reset edge count
dfs(i, i, -1)
isArt[i] = (outEdgeCount > 1)
return isArt
# Perform DFS to find articulation points.
function dfs(root, at, parent):
if (parent == root): outEdgeCount++
visited[at] = true
id = id + 1
low[at] = ids[at] = id
# For each edge from node ‘at’ to node ‘to’
for (to : g[at]):
if to == parent: continue
if (!visited[to]):
dfs(root, to, at)
low[at] = min(low[at], low[to])
# Articulation point found via bridge
if (ids[at] < low[to]):
isArt[at] = true
# Articulation point found via cycle
if (ids[at] == low[to]):
isArt[at] = true
else:
low[at] = min(low[at], ids[to])
Being explicit here.
However, this could just
be a <= clause.
Source Code Link
Slides/source code can be
found at the following link:
github.com/williamfiset/algorithms
Link in the description:
0
1
2 5
3
6
4
8
7
Bridges and
Articulation Points
Bridges and
Articulation
Points
Source Code
Graph Theory
Video Series
Tarjan’s Algorithm
for Finding Strongly
Connected Components
William Fiset
What are SCCs?
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
What are SCCs?
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
0
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0
2
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0
2 3
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
0
The low-link of node 1 is 0, since node 0 is
node with the lowest id reachable from node 1
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
The low-link of node 3 is 2, since node 2 is
node with the lowest id reachable from node 3
1
0 4
2
6 3
5
0 0
0 2 2
4
4
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
0 0
0 2 2
4
4
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
1
0 4
2
6 3
5
0 0
0 2 2
4
4
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
4
6 1
3
5 2
0
Started DFS on the rightmost node, resumed DFS
on node 2, and then resumed DFS on node 4 to
finish labelling all nodes in the graph.
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
4
6 1
3
5 2
0
0
Low-Link Values
The low-link value of a node is the smallest
[lowest] node id reachable from that node when
doing a DFS (including itself).
All low link values
are the same, but
there are multiple
SCCs!
4
6 1
3
5 2
0
IMPORTANT: Depending on where the DFS starts, and the order
in which nodes/edges are visited, the low-link values for
identifying SCCs could be wrong. In the context of Tarjan’s
SCC algorithm, we maintain an invariant that prevents SCCs
to interfere with the low link value of other SCCs.
0 0 0
0
0
0
0
The Stack Invariant
To cope with the random traversal order of
the DFS, Tarjan’s algorithm maintains a set
(often as a stack) of valid nodes from which
to update low-link values from.
Nodes are added to the stack [set] of valid
nodes as they’re explored for the first time.
Nodes are removed from the stack [set] each
time a complete SCC is found.
If u and v are nodes in a graph and we’re
currently exploring u then our new low-
link update condition is that:
New low-link update condition
To update node u’s low-link value to node
v’s low-link value there has to be a path
of edges from u to v and node v must be on
the stack.
Another difference we’re going to make to
finding all low-link values is that instead
of finding low-link values after the fact
we’re going to update them “on the fly”
during the DFS so we can get a linear
O(V+E) time complexity :)
Time Complexity
Tarjan’s Algorithm Overview
Mark the id of each node as unvisited.
Start DFS. Upon visiting a node assign it an
id and a low-link value. Also mark current
nodes as visited and add them to a seen stack.
On DFS callback, if the previous node is on
the stack then min the current node’s low-link
value with the last node’s low-link value*.
After visiting all neighbors, if the current
node started a connected component** then pop
nodes off stack until current node is reached.
**As we will see, a node started a connected component if
its id equals its low link value
*This allows low-link values to propagate throughout cycles.
Stack
Unvisited
Visiting
neighbours
Visited all
neighbours
If a node’s colour is grey or orange then
it is on the stack and we can update its
low-link value.
0
0
Stack
0
Unvisited
Visiting
neighbours
Visited all
neighbours
Start DFS anywhere.
0
0
Stack
1
0
Unvisited
Visiting
neighbours
Visited all
neighbours
0 1
0
Stack
1
0 1
Unvisited
Visiting
neighbours
Visited all
neighbours
0 1
0
Stack
1
0 1
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 1
2
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 1
2
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 1
0
lowlink[2] = min(lowlink[2], lowlink[0])
= 0
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 1
0
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 0
0
lowlink[1] = min(lowlink[1], lowlink[2])
= 0
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 0
0
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 0
0
lowlink[0] = min(lowlink[0], lowlink[1])
= 0
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
2
0 0
0
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
1
0 0
0
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
0
Stack
0 0
0
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
Stack
0 0
0
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
0
2
1
Stack
0 0
0
We’re not done exploring the graph so pick
another starting node at random.
Unvisited
Visiting
neighbours
Visited all
neighbours
3
0
2
1
Stack
0 0
0
3
3
Unvisited
Visiting
neighbours
Visited all
neighbours
3
0
2
1
Stack
0 0
0
3
3
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3
0
2
1
Stack
0 0
0
3
3
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3
0
2
1
Stack
0 0
0
3
3
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
Unvisited
Visiting
neighbours
Visited all
neighbours
Node 0 is not on stack so don’t min
with its low-link value.
4
3
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
6
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
6
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
6
6
Node 2 is not on stack so don’t min
with its low-link value.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
6
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
4
6
lowlink[6] = min(lowlink[6], lowlink[4])
= 4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
4
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
4
6
Node 0 is not on stack so don’t min
with its low-link value.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
5
4
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
4
4
6
lowlink[5] = min(lowlink[5], lowlink[6])
= 4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
4
4
6
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
4
4
6
lowlink[4] = min(lowlink[4], lowlink[5])
= 4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
4
4
6
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
5
4
4
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
4
4
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
4
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
4
Node 4 is not on stack so don’t min
with its low-link value.
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5 0
2
1
Stack
0 0
0
3
3
4
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
7
7
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
7
7
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
Node 5 is not on stack so don’t min
with its low-link value.
Unvisited
Visiting
neighbours
Visited all
neighbours
7
7
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
Unvisited
Visiting
neighbours
Visited all
neighbours
7
7
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
3
7
lowlink[6] = min(lowlink[6], lowlink[3])
= 3
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
3
7
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
Stack
0 0
0
3
3
4
4
4
3
lowlink[3] = min(lowlink[3], lowlink[6])
= 3
Unvisited
Visiting
neighbours
Visited all
neighbours
7
4
3 6
5
7 0
2
1
Stack
0 0
0
3
4
4
4
7
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
3
3
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
Stack
0 0
0
3
4
4
4
When a completed SCC is found (current node
has visited all its neighbours and its lowlink
value equals its id) pop off all associated
nodes off the stack.
3
3
Unvisited
Visiting
neighbours
Visited all
neighbours
4
3 6
5
7 0
2
1
0 0
0
3 4
4
4
3
Unvisited
Visiting
neighbours
Visited all
neighbours
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
UNVISITED = -1
n = number of nodes in graph
g = adjacency list with directed edges
id = 0 # Used to give each node an id
sccCount = 0 # Used to count number of SCCs found
# Index i in these arrays represents node i
ids = [0, 0, … 0, 0] # Length n
low = [0, 0, … 0, 0] # Length n
onStack = [false, false, …, false] # Length n
stack = an empty stack data structure
function findSccs():
for(i = 0; i < n; i++): ids[i] = UNVISITED
for(i = 0; i < n; i++):
if(ids[i] == UNVISITED):
dfs(i)
return low
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
function dfs(at):
stack.push(at)
onStack[at] = true
ids[at] = low[at] = id++
# Visit all neighbours & min low-link on callback
for(to : g[at]):
if(ids[to] == UNVISITED): dfs(to)
if(onStack[to]): low[at] = min(low[at],low[to])
# After having visited all the neighbours of ‘at’
# if we're at the start of a SCC empty the seen
# stack until we’re back to the start of the SCC.
if(ids[at] == low[at]):
for(node = stack.pop();;node = stack.pop()):
onStack[node] = false
low[node] = ids[at]
if(node == at): break
sccCount++
Tarjan’s Strongly
Connected Component
Algorithm (UPDATED)
Tarjan’s SCC
Algorithm
Source Code
Graph Theory
Video Series
Strongly Connected
Components (SCCs)
William Fiset
Kosaraju’s algorithm
What are SCCs?
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
What are SCCs?
Strongly Connected Components (SCCs) can be
thought of as self-contained cycles within a
directed graph where every vertex in a given cycle
can reach every other vertex in the same cycle.
Kosaraju’s algorithm intuition
0 1 3
2 6
4 5
7
We are able to find a graph traversal ordering
which enables us to visit SCCs
Kosaraju’s algorithm intuition
0 1
6
2 3
7 8
4 5
9
0 1
6
2 3
7 8
4 5
9
Kosaraju’s algorithm intuition
0 1
6
2 3
7 8
4 5
9
0 1
6
2 3
7 8
4 5
9
[0, 1, 6, 2, 3, 7, 4, 5, 9, 8]
Kosaraju’s algorithm intuition
1 0 2
4
5
3
[1, 3, 0, 5, 2, 4]
1 0 2
4
5
3
Nodes than can reach
Kosaraju’s algorithm intuition
0 2 1
7
4
3
6
5
[0, 3, 2, 1, 7, 6, 5, 4]
0 2 1
7
4
3
6
5
The reverse post order ordering presents the nodes in a
order in which the node(s) which leak the most appear
first. In G^T, these nodes will be those which are part
the "smallest SCC", that is the SCC which leak the least
Kosaraju’s algorithm intuition
[0, 3, 2, 1, 7, 6, 5, 4]
Graph Theory
Video Series
Traveling Salesman
Problem (TSP) with
Dynamic Programming
William Fiset
What is the TSP?
"Given a list of cities and the
distances between each pair of cities,
what is the shortest possible route
that visits each city exactly once and
returns to the origin city?” - Wiki
What is the TSP?
In other words, the problem is: given a
complete graph with weighted edges (as an
adjacency matrix) what is the Hamiltonian
cycle (path that visits every node once) of
minimum cost?
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
A
C
B
D
4
9
1
3
6 11
4 1
2
6
5
-4
What is the TSP?
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
A
C
B
D
9
3
1
-4
Tour cost: 9 + -4 + 1 + 3 = 9
Full tour: A -> D -> C -> B -> A
In other words, the problem is: given a
complete graph with weighted edges (as an
adjacency matrix) what is the Hamiltonian
cycle (path that visits every node once) of
minimum cost?
What is the TSP?
Finding the optimal solution to the TSP
problem is very hard; in fact, the
problem is known to be NP-Complete.
Brute force solution
The brute force way to solve the TSP is to compute
the cost of every possible tour. This means we have
to try all possible permutations of node orderings
which takes O(n!) time.
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A B C D
A
B
C
D
Tour Cost
A B C D 18 C A B D 15
A B D C 15 C A D B 24
A C B D 19 C B A D 9
A C D B 11 C B D A 19
A D B C 24 C D A B 18
A D C B 9 C D B A 11
B A C D 11 D A B C 18
B A D C 9 D A C B 19
B C A D 24 D B A C 11
B C D A 18 D B C A 24
B D A C 19 D C A B 15
B D C A 15 D C B A 9
TSP with DP
The dynamic programming solution to the TSP
problem significantly improves on the time
complexity, taking it from O(n!) to O(n22n).
At first glance, this may not seem like a
substantial improvement, however, it now
makes solving this problem feasible on
graphs with up to roughly 23 nodes on a
typical computer.
TSP with DP
n n! n22n
1 1 2
2 2 16
3 6 72
4 24 256
5 120 800
6 720 2304
… … …
15 1307674368000 7372800
16 20922789888000 16777216
17 355687428096000 37879808
TSP with DP
The main idea will be to compute the optimal
solution for all the subpaths of length N
while using information from the already
known optimal partial tours of length N-1.
TSP with DP
Before starting, make sure to select a node
0 ≤ S < N to be the designated starting node
for the tour.
TSP with DP
0
2
1
3
Start of tour
Before starting, make sure to select a node
0 ≤ S < N to be the designated starting node
for the tour.
For this example let S = node 0
TSP with DP
0
2
1
3
Next, compute and store the optimal value
from S to each node X (≠ S). This will solve
TSP problem for all paths of length n = 2.
TSP with DP
0
2
1
3
0
2
1
3
0
2
1
3
Next, compute and store the optimal value
from S to each node X (≠ S). This will solve
TSP problem for all paths of length n = 2.
TSP with DP
To compute the optimal solution for paths of
length 3, we need to remember (store) two
things from each of the n = 2 cases:
1) The set of visited nodes in the subpath
2) The index of the last visited node in
the path
Together these two things form our dynamic
programming state. There are N possible nodes
that we could have visited last and 2N
possible subsets of visited nodes. Therefore
the space needed to store the answer to each
subproblem is bounded by O(N2N).
The best way to represent the set of visited
nodes is to use a single 32-bit integer. A
32-bit int is compact, quick and allows for
easy caching in a memo table.
Visited Nodes as a Bit Field
Visited Nodes as a Bit Field
The best way to represent the set of visited
nodes is to use a single 32-bit integer. A
32-bit int is compact, quick and allows for
easy caching in a memo table.
0
2
1
3
0
2
1
3
0
2
1
3
State
Binary rep: 00112 = 3
Last node: 1
State
Binary rep: 10012 = 9
Last node: 3
State
Binary rep: 01012 = 5
Last node: 2
TSP with DP
0
2
1
3
State
Binary rep: 10012 = 9
Last node: 3
To solve 3 ≤ n ≤ N, we’re going to take the solved
subpaths from n-1 and add another edge extending
to a node which has not already been visited from
the last visited node (which has been saved).
TSP with DP
0
2
1
3
State
Binary rep: 10012 = 9
Last node: 3
0
2
1
3
State
Binary rep: 10112 = 11
Last node: 1
0
2
1
3
State
Binary rep: 11012 = 13
Last node: 2
To solve 3 ≤ n ≤ N, we’re going to take the solved
subpaths from n-1 and add another edge extending
to a node which has not already been visited from
the last visited node (which has been saved).
To complete the TSP tour, we need to connect
our tour back to the start node S.
TSP with DP
Loop over the end state* in the memo table
for every possible end position and minimize
the lookup value plus the cost of going back
to S.
* The end state is the state where the binary
representation is composed of N 1’s
In the next few slides we’ll look at some
pseudocode for the TSP problem.
TSP Pseudocode
WARNING: The following slides make use of
advanced bit manipulation techniques. Make
sure you’re very comfortable with the binary
operators <<, &, | and ^
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
# Initializes the memo table by caching
# the optimal solution from the start
# node to every other node.
function setup(m, memo, S, N):
for (i = 0; i < N; i = i + 1):
if i == S: continue
# Store the optimal value from node S
# to each node i (this is given as input
# in the adjacency matrix m).
memo[i][1 << S | 1 << i] = m[S][i]
0
2
1
3
0
2
1
3
0
2
1
3
State
Binary rep: 00112 = 3
Last node: 1
State
Binary rep: 10012 = 9
Last node: 3
State
Binary rep: 01012 = 5
Last node: 2
Node S Node i
# Initializes the memo table by caching
# the optimal solution from the start
# node to every other node.
function setup(m, memo, S, N):
for (i = 0; i < N; i = i + 1):
if i == S: continue
# Store the optimal value from node S
# to each node i (this is given as input
# in the adjacency matrix m).
memo[i][1 << S | 1 << i] = m[S][i]
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
function solve(m, memo, S, N):
for (r = 3; r <= N; r++):
# The combinations function generates all bit sets
# of size N with r bits set to 1. For example,
# combinations(3, 4) = {01112, 10112, 11012, 11102}
for subset in combinations(r, N):
if notIn(S, subset): continue
for (next = 0; next < N; next = next + 1):
if next == S || notIn(next, subset): continue
# The subset state without the next node
state = subset ^ (1 << next)
minDist = +∞
# ‘e’ is short for end node.
for (e = 0; e < N; e = e + 1):
if e == S || e == next || notIn(e, subset)):
continue
newDistance = memo[e][state] + m[e][next]
if (newDistance < minDist): minDist = newDistance
memo[next][subset] = minDist
# Returns true if the ith bit in ‘subset’ is not set
function notIn(i, subset):
return ((1 << i) & subset) == 0
function solve(m, memo, S, N):
for (r = 3; r <= N; r++):
# The combinations function generates all bit sets
# of size N with r bits set to 1. For example,
# combinations(3, 4) = {01112, 10112, 11012, 11102}
for subset in combinations(r, N):
if notIn(S, subset): continue
for (next = 0; next < N; next = next + 1):
if next == S || notIn(next, subset): continue
# The subset state without the next node
state = subset ^ (1 << next)
minDist = +∞
# ‘e’ is short for end node.
for (e = 0; e < N; e = e + 1):
if e == S || e == next || notIn(e, subset)):
continue
newDistance = memo[e][state] + m[e][next]
if (newDistance < minDist): minDist = newDistance
memo[next][subset] = minDist
# Returns true if the ith bit in ‘subset’ is not set
function notIn(i, subset):
return ((1 << i) & subset) == 0
# Generate all bit sets of size n with r bits set to 1.
function combinations(r, n):
subsets = []
combinations(0, 0, r, n, subsets)
return subsets
# Recursive method to generate bit sets.
function combinations(set, at, r, n, subsets):
if r == 0:
subsets.add(set)
else:
for (i = at; i < n; i = i + 1):
# Flip on ith bit
set = set | (1 << i)
combinations(set, i + 1, r - 1, n, subsets)
# Backtrack and flip off ith bit
set = set & ~(1 << i)
NOTE: For a more detailed explanation on generating
combinations see video “Backtracking tutorial: power set”
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
function findMinCost(m, memo, S, N):
# The end state is the bit mask with
# N bits set to 1 (equivalently 2N - 1)
END_STATE = (1 << N) - 1
minTourCost = +∞
for (e = 0; e < N; e = e + 1):
if e == S: continue
tourCost = memo[e][END_STATE] + m[e][S]
if tourCost < minTourCost:
minTourCost = tourCost
return minTourCost
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
function findOptimalTour(m, memo, S, N):
lastIndex = S
state = (1 << N) - 1; # End state
tour = array of size N+1
for (i = N-1; i >= 1; i--):
index = -1
for (j = 0; j < N; j++):
if j == S || notIn(j, state): continue
if (index == -1) index = j
prevDist = memo[index][state] + m[index]lastIndex]
newDist = memo[j][state] + m[j][lastIndex];
if (newDist < prevDist) index = j
tour[i] = index
state = state ^ (1 << index)
lastIndex = index
tour[0] = tour[N] = S
return tour
# Finds the minimum TSP tour cost.
# m - 2D adjacency matrix representing graph
# S - The start node (0 ≤ S < N)
function tsp(m, S):
N = matrix.size
# Initialize memo table.
# Fill table with null values or +∞
memo = 2D table of size N by 2N
setup(m, memo, S, N)
solve(m, memo, S, N)
minCost = findMinCost(m, memo, S, N)
tour = findOptimalTour(m, memo, S, N)
return (minCost, tour)
Next video: Source Code!
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description:
Traveling Salesman
Problem Dynamic
Programming
0 4 1 9
3 0 6 11
4 1 0 2
6 5 -4 0
A
C
B
D
TSP Dynamic
Programming
Source code
Graph Theory
Video Series
Existence of
Eulerian Paths
and Circuits
William Fiset
What is an Eulerian Path?
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
S
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
S
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
S
S
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
S
S
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
S
S
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
S
S
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
We can find an Eulerian
path on the following
graph, but only if we
start at specific nodes.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
S
Suppose we start another
path but this time at a
different node.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
S
Suppose we start another
path but this time at a
different node.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
S
Choosing the wrong
starting node can lead to
having unreachable edges.
An Eulerian Path (or Eulerian Trail) is a path
of edges that visits all the edges in a graph
exactly once.
What is an Eulerian Path?
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
If you know the graph contains an Eulerian
cycle then you can start anywhere.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
s+e
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
What is an Eulerian circuit?
If your graph does not contain an Eulerian cycle then
you may not be able to return to the start node or
you will not be able to visit all edges of the graph.
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
Oops, we’re stuck and can’t
make it back to start node
What is an Eulerian circuit?
Similarly, an Eulerian circuit (or Eulerian
cycle) is an Eulerian path which starts and
ends on the same vertex.
There are also
unvisited edges
remaining
Oops, we’re stuck and can’t
make it back to start node
What is an Eulerian circuit?
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of the
Euler path/circuit problem we care about:
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
Node Degrees
Node Degrees
Undirected graph
The degree of a node is
how many edges are
attached to it.
Node degree = 3
Node Degrees
Undirected graph
The degree of a node is
how many edges are
attached to it.
Directed graph
Node degree = 3
In degree = 2
Out degree = 1
The indegree is the
number of incoming edges
and outdegree is number
of outgoing edges.
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of Euler
path/circuit problem we care about:
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of Euler
path/circuit problem we care about:
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of Euler
path/circuit problem we care about:
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of Euler
path/circuit problem we care about:
Eulerian Circuit Eulerian Path
Undirected
Graph
Every vertex has an even
degree.
Either every vertex has even
degree or exactly two vertices
have odd degree.
Directed
Graph
Every vertex has equal
indegree and outdegree
At most one vertex has
(outdegree) - (indegree) = 1
and at most one vertex has
(indegree) - (outdegree) = 1
and all other vertices have
equal in and out degrees.
What conditions are required for
a valid Eulerian Path/Circuit?
That depends on what kind of graph you’re dealing
with. Altogether there are four flavors of Euler
path/circuit problem we care about:
Does this undirected graph have
an Eulerian path/circuit?
No Eulerian path or circuit.
There are too many nodes that
have an odd degree.
Does this undirected graph have
an Eulerian path/circuit?
Does this undirected graph have
an Eulerian path/circuit?
S/E
S/E
Only Eulerian path.
Start and
End nodes
Does this undirected graph have
an Eulerian path/circuit?
Does this undirected graph have
an Eulerian path/circuit?
Yes! It has both an
Eulerian path and circuit.
Does this undirected graph have
an Eulerian path/circuit?
True or false: if a graph has an Eulerian
circuit, it also has an Eulerian path.
Does this undirected graph have
an Eulerian path/circuit?
Does this undirected graph have
an Eulerian path/circuit?
True or false: if a graph has an Eulerian
circuit, it also has an Eulerian path.
Does this undirected graph have
an Eulerian path/circuit?
There are no Eulerian paths/circuits. An
additional requirement when finding paths/
circuits is that all vertices with nonzero
degree need to belong to a single
connected component.
Does this undirected graph have
an Eulerian path/circuit?
Does this directed graph have
an Eulerian path/circuit?
Yes, it has both an Eulerian path
and an Eulerian circuit because all
in/out degrees are equal.
Does this directed graph have
an Eulerian path/circuit?
Does this directed graph have
an Eulerian path/circuit?
No path or circuit. The red nodes have either
too many in coming or outgoing edges.
Does this directed graph have
an Eulerian path/circuit?
Does this directed graph have
an Eulerian path/circuit?
S
E
This graph has an Eulerian path, but no
Eulerian circuit. It also has a unique
start/end node for the path.
Does this directed graph have
an Eulerian path/circuit?
S
E
Note that the singleton node has no incoming/
outgoing edges, so it doesn’t impact whether
or not we have an Eulerian path.
Does this directed graph have
an Eulerian path/circuit?
Next Video: Eulerian path algorithm
S
E
Existence of Eulerian
Paths and Circuits
Graph Theory
Video Series
Finding
Eulerian Paths
and Circuits
William Fiset
Previous video:
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Step 1 to finding an Eulerian path is determining
if there even exists an Eulerian path.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Recall that for an Eulerian path to exist at most one
one vertex has (outdegree) - (indegree) = 1 and at
most one vertex has (indegree) - (outdegree) = 1 and
all other vertices have equal in and out degrees.
Step 1 to finding an Eulerian path is determining
if there even exists an Eulerian path.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1
2
3
4
5
6
Count the in/out degrees of each node by
looping through all the edges.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1
2 1
3
4
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 2
2 1
3 1
4
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1 2
2 1
3 1 1
4
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1 2
2 2
3 1 2
4
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1 2
2 2 1
3 1 2
4 1
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1 2
2 3 2
3 1 2
4 1
5
6
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0
1 1 2
2 3 2
3 1 2
4 1
5
6
And so on for all other edges…
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Once we’ve verified that no node has too many
outgoing edges (out[i] - in[i] > 1) or incoming
edges (in[i] - out[i] > 1) and there are just
the right amount of start/end nodes we can be
certain that an Eulerian path exists.
The next step is to find a valid starting node.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Node 1 is the only node with exactly one extra outgoing
edge, so it’s our only valid start node. Similarly,
node 6 is the only node with exactly one extra incoming
edge, so it will end up being the end node.
out[1] - in[1] = 2 - 1 = 1
in[6] - out[6] = 2 - 1 = 1
Start node
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
NOTE: If all in and out degrees are equal (Eulerian
circuit case) then any node with non-zero degree
would serve as a suitable starting node.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Now that we know the starting
node, let’s find an Eulerian path!
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Let’s see what happens if we do a naive
DFS, trying to traverse as many edges as
possible until we get stuck.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
By randomly selecting edges during the
DFS we made it from the start node to
the end node.
However, we did not find an Eulerian
path because we didn’t traverse all the
edges in our graph!
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
The good news is we can modify our DFS
to handle forcing the traversal of all
edges :)
Finding an Eulerian path (directed graph)
0 1 3 4
2
To illustrate this, consider starting at
node 0 and trying to find an Eulerian path.
Finding an Eulerian path (directed graph)
0 1 3 4
2
To illustrate this, consider starting at
node 0 and trying to find an Eulerian path.
Finding an Eulerian path (directed graph)
0 1 3 4
2
To illustrate this, consider starting at
node 0 and trying to find an Eulerian path.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Whoops… we skipped the edges going to
node 2 and back which need to be part
of the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [3, 4]
When backtracking, if the current node has
any remaining unvisited edges (white edges)
we follow any of them calling our DFS method
recursively to extend the Eulerian path.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [3, 4]
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [3, 4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [1, 3, 4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [2, 1, 3, 4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [1, 2, 1, 3, 4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0 1 3 4
2
Solution: [0, 1, 2, 1, 3, 4]
Once we get stuck (meaning the current
node has no unvisited outgoing edges),
we backtrack and add the current node to
the solution.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
Coming back to the previous example, let’s
restart the algorithm, but this time track
the number of unvisited edges we have left to
take for each node.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node In Out
0 0 0
1 1 2
2 3 3
3 3 3
4 2 2
5 1 1
6 2 1
In fact, we have already computed the number
of outgoing edges for each edge in the “out”
array which we can reuse.
We won’t be needing the “in” array after
we’ve validated that an Eulerian path exists.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 2
2 3
3 3
4 2
5 1
6 1
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 2
2 3
3 3
4 2
5 1
6 1
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 3
3 3
4 2
5 1
6 1
Every time an edge is taken, reduce the
outgoing edge count in the out array.
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 3
3 2
4 2
5 1
6 1
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 3
3 2
4 2
5 0
6 1
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 3
3 2
4 2
5 0
6 0
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 3
3 1
4 2
5 0
6 0
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 2
3 1
4 2
5 0
6 0
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 1
2 2
3 1
4 1
5 0
6 0
Solution = []
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = []
Node Out
0 0
1 1
2 2
3 1
4 1
5 0
6 0
When the DFS is stuck, meaning there are
no more outgoing edges (i.e out[i] = 0),
then we know to backtrack and add the
current node to the solution.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 1
2 2
3 1
4 1
5 0
6 0
When backtracking, if the current node has
any remaining unvisited edges (white edges),
we follow any of them, calling our DFS method
recursively to extend the Eulerian path. We
can verify there still are outgoing edges by
checking if out[i] != 0.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 1
2 2
3 1
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 1
2 2
3 0
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 0
2 2
3 0
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 0
2 1
3 0
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [6]
Node Out
0 0
1 0
2 1
3 0
4 0
5 0
6 0
When the DFS is stuck, meaning there are
no more outgoing edges (i.e out[i] = 0),
then we know to backtrack and add the
current node to the solution.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [4,6]
Node Out
0 0
1 0
2 1
3 0
4 0
5 0
6 0
Node 2 still has an unvisited edge (since
out[i] != 0) so we need to follow that edge.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [4,6]
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
When the DFS is stuck, meaning there are
no more outgoing edges (i.e out[i] = 0),
then we know to backtrack and add the
current node to the solution.
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [2,4,6]
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Solution = [2,2,4,6]
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [3,2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [6,3,2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [5,6,3,2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [3,5,6,3,2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [1,3,5,6,3,2,4,3,1,2,2,4,6]
Finding an Eulerian path (directed graph)
0
1
2
3
4
5
6
Node Out
0 0
1 0
2 0
3 0
4 0
5 0
6 0
Solution = [1,3,5,6,3,2,4,3,1,2,2,4,6]
The time complexity to find an eulerian
path with this algorithm is O(E). The two
calculations we’re doing: computing in/out
degrees + DFS are both linear in the
number of edges.
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
function countInOutDegrees():
for edges in g:
for edge in edges:
out[edge.from]++
in[edge.to]++
function graphHasEulerianPath():
start_nodes, end_nodes = 0, 0
for (i = 0; i < n; i++):
if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1:
return false
else if out[i] - in[i] == 1:
start_nodes++
else if in[i] - out[i] == 1:
end_nodes++
return (end_nodes == 0 and start_nodes == 0) or
(end_nodes == 1 and start_nodes == 1)
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
Avoids starting DFS
at a singleton
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
The out array is currently serving two purposes. One
purpose is to track whether or not there are still
outgoing edges, and the other is to index into the
adjacency list to select the next outgoing edge.
This assumes the adjacency list stores edges in a
data structure that is indexable in O(1) (e.g stored
in an array). If not (e.g a linked-list/stack/etc…),
you can use an iterator to iterate over the edges.
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
function findStartNode():
start = 0
for (i = 0; i < n; i = i + 1):
# Unique starting node
if out[i] - in[i] == 1: return i
# Start at any node with an outgoing edge
if out[i] > 0: start = i
return start
function dfs(at):
# While the current node still has outgoing edges
while (out[at] != 0):
# Select the next unvisited outgoing edge
next_edge = g[at].get(——out[at])
dfs(next_edge.to)
# Add current node to solution
path.insertFirst(at)
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
# Global/class scope variables
n = number of vertices in the graph
m = number of edges in the graph
g = adjacency list representing directed graph
in = [0, 0, …, 0, 0] # Length n
out = [0, 0, …, 0, 0] # Length n
path = empty integer linked list data structure
function findEulerianPath():
countInOutDegrees()
if not graphHasEulerianPath(): return null
dfs(findStartNode())
# Return eulerian path if we traversed all the
# edges. The graph might be disconnected, in which
# case it’s impossible to have an euler path.
if path.size() == m+1: return path
return null
Source Code Link
Implementation source code can
be found at the following link:
github.com/williamfiset/algorithms
Link in the description:
Eulerian Paths
and Circuits
source code
William Fiset
0
1
2
3
4
5
6
Finding Eulerian Paths
and Circuits
Graph Theory
Video Series
Prim’s Minimum
Spanning Tree
Algorithm
William Fiset
(lazy version)
What is a Minimum Spanning Tree?
Given an undirected graph with weighted edges,
a Minimum Spanning Tree (MST) is a subset of
the edges in the graph which connects all
vertices together (without creating any
cycles) while minimizing the total edge cost.
What is a Minimum Spanning Tree?
0
2
5
3
1
6
4
9
4
1
2 3
0
7
6
-2
6
5 3
Given an undirected graph with weighted edges,
a Minimum Spanning Tree (MST) is a subset of
the edges in the graph which connects all
vertices together (without creating any
cycles) while minimizing the total edge cost.
What is a Minimum Spanning Tree?
MST cost: 0 + 5 + -2 + 2 + 1 + 3 = 9
0
2
5
3
1
6
4
9
4
1
2 3
0
7
6
-2
6
5 3
This particular graph has a unique MST
highlighted in green. However, it is common
for a graph to have multiple valid MSTs of
equal costs.
Find any MST
1
0
3
2
5
4
3
8
0
2
2
5
1
6
4
Find any MST
1
0
3
2
5
4
3
8
0
2
2
5
1
6
4
1
0
3
2
5
4
3
8
0
2
2
5
1
6
4
MST cost: 3 + 1 + 2 + 0 + 8 = 14
1
0
4
3
Find any MST
2
5
7
6 8
1
2
3
4
5
6
7
8 9 10
11
12
1
0
4
3
Find any MST
2
5
7
6 8
1
2
3
4
5
6
7
8 9 10
11
12
1
0
4
3
2
5
7
6 8
1
2
3
4
5
6
7
8 9 10
11
12
MST cost: 3 + 1 + 2 + 4 + 7 + 8 + 9 + 5 = 39
1
0
3
2
5
4
1
1
-5
10 1
10
Find any MST
This graph has no MST!
All nodes must be
connected to form a
spanning tree.
1
0
3
2
5
4
1
1
-5
10 1
10
Find any MST
Prim’s MST Algorithm
Prim’s is a greedy MST algorithm that works
well on dense graphs. On these graphs, Prim’s
meets or improves on the time bounds of its
popular rivals (Kruskal’s & Borůvka’s).
However, when it comes to finding the minimum
spanning forest on a disconnected graph,
Prim’s cannot do this as easily (the
algorithm must be run on each connected
component individually).
The lazy version of Prim’s has a runtime
of O(E*log(E)), but the eager version
(which we will also look at) has a
better runtime of O(E*log(V)).
Lazy Prim’s MST Overview
Start the algorithm on any node s. Mark s as visited
and iterate over all edges of s, adding them to the PQ.
Maintain a min Priority Queue (PQ) that sorts edges
based on min edge cost. This will be used to
determine the next node to visit and the edge used
to get there.
While the PQ is not empty and a MST has not been
formed, dequeue the next cheapest edge from the PQ. If
the dequeued edge is outdated (meaning the node it
points to has already been visited) then skip it and
poll again. Otherwise, mark the current node as visited
and add the selected edge to the MST.
Iterate over the new current node’s edges and add all
its edges to the PQ. Do not add edges to the PQ which
point to already visited nodes.
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
One thing to bear in mind is that although
the graph above represents an undirected
graph, the internal adjacency list
representation typically has each undirected
edge stored as two directed edges.
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
The actual internal representation
typically looks like this.
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
On the right we will be keeping track of the
PQ containing the edge objects as triplets:
(start node, end node, edge cost)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
Unvisited Visiting Visited
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
Let’s begin Prim’s at node 0.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
Iterate over all outgoing edges
and add them to the PQ.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
Edge (0, 2, 1) has the lowest value in the PQ
so it gets added to the MST. This also means
that the next node we process is node 2.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
Next, iterate through all the edges of node 2
and add them to the PQ.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
Node 0 is already visited so we skip
adding the edge (2, 0, 1) to the PQ.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
The reason we don't include edges which
point to already visited nodes is that
either they overlap with an edge already
part of the MST (as is the case with the
edge on this slide) or it would introduce a
cycle in the MST, if included.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
Edge (2, 3, 2) has the lowest value in the PQ
so it gets added to the MST. This also means
that the next node we process is node 3.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
The same process of adding edges to the PQ and
polling the smallest edge continues until the
MST is complete.
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
(2, 1, 3)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
The edge (2, 1, 3) is stale, poll again.
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
The edge (0, 3, 4) is also stale, poll again.
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
The edge (3, 6, 7) is also stale, poll again.
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
There’s a tie between (2, 5, 8) and (4, 7, 8)
for which edge gets polled next. Since (2, 5, 8)
was added first, let’s assume it gets priority
(it doesn’t matter in practice).
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
The edge (2, 5, 8) is stale, poll again.
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
(2, 5, 8)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
10 3
1
4
2
2
8
7
6
12
9
8
1
0
We can now stop Prim’s since the MST is
complete. We know the MST is complete
because the number of edges in the tree is
one less than the number of nodes in the
graph (i.e. the definition of a tree).
(6, 7, 12)
(4, 7, 8)
(4, 1, 0)
(0, 1, 10)
(0, 2, 1)
(0, 3, 4)
(2, 1, 3)
(2, 5, 8)
(2, 3, 2)
(3, 5, 2)
(3, 6, 7)
(5, 4, 1)
(5, 6, 6)
(5, 7, 9)
Edges in PQ
(start, end, cost)
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
If we collapse the graph back into the
undirected edge view it becomes clear which
edges are included in the MST.
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
Lazy Prim’s
1
0 2
3
4
5
6
7
10
1
3
12
4
2 2
7
8
0
1
8
9
6
The MST cost is:
1 + 2 + 2 + 6 + 1 + 0 + 8 = 20
Lazy Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
pq = … # PQ data structure; stores edge objects consisting of
# {start node, end node, edge cost} tuples. The PQ sorts
# edges based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Lazy Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
pq = … # PQ data structure; stores edge objects consisting of
# {start node, end node, edge cost} tuples. The PQ sorts
# edges based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Lazy Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
pq = … # PQ data structure; stores edge objects consisting of
# {start node, end node, edge cost} tuples. The PQ sorts
# edges based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Lazy Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
pq = … # PQ data structure; stores edge objects consisting of
# {start node, end node, edge cost} tuples. The PQ sorts
# edges based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Lazy Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
pq = … # PQ data structure; stores edge objects consisting of
# {start node, end node, edge cost} tuples. The PQ sorts
# edges based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
function addEdges(nodeIndex):
# Mark the current node as visited.
visited[nodeIndex] = true
# Iterate over all edges going outwards from the current node.
# Add edges to the PQ which point to unvisited nodes.
edges = g[nodeIndex]
for (edge : edges):
if !visited[edge.to]:
pq.enqueue(edge)
Helper method to iterate over the edges of a
node and add edges to the PQ:
function addEdges(nodeIndex):
# Mark the current node as visited.
visited[nodeIndex] = true
# Iterate over all edges going outwards from the current node.
# Add edges to the PQ which point to unvisited nodes.
edges = g[nodeIndex]
for (edge : edges):
if !visited[edge.to]:
pq.enqueue(edge)
Helper method to iterate over the edges of a
node and add edges to the PQ:
function addEdges(nodeIndex):
# Mark the current node as visited.
visited[nodeIndex] = true
# Iterate over all edges going outwards from the current node.
# Add edges to the PQ which point to unvisited nodes.
edges = g[nodeIndex]
for (edge : edges):
if !visited[edge.to]:
pq.enqueue(edge)
Helper method to iterate over the edges of a
node and add edges to the PQ:
function addEdges(nodeIndex):
# Mark the current node as visited.
visited[nodeIndex] = true
# Iterate over all edges going outwards from the current node.
# Add edges to the PQ which point to unvisited nodes.
edges = g[nodeIndex]
for (edge : edges):
if !visited[edge.to]:
pq.enqueue(edge)
Helper method to iterate over the edges of a
node and add edges to the PQ:
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
Skip edge that points to
a visited node.
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function lazyPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
addEdges(s)
while (!pq.isEmpty() and edgeCount != m):
edge = pq.dequeue()
nodeIndex = edge.to
if visited[nodeIndex]:
continue
mstEdges[edgeCount++] = edge
mstCost += edge.cost
addEdges(nodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
Next Video: Eager Prim’s
Prim’s Minimum Spanning Tree
Algorithm (lazy version)
1
0 2
3
4
5
6
7
10
1
3
12
4
2
2
7
8
0
1
8
9
6
10
3
1
4
2
2
8
7
6
12
9
8
1
0
Graph Theory
Video Series
Prim’s Minimum
Spanning Tree
Algorithm
William Fiset
(eager version)
Previous Video: Lazy Prim’s MST
<insert video clip>
Link to lazy Prim’s in the description
The lazy implementation of Prim’s inserts up
to E edges into the PQ. Therefore, each poll
operation on the PQ is O(log(E)).
Eager Prim’s
Instead of blindly inserting edges into a
PQ which could later become stale, the
eager version of Prim’s tracks (node, edge)
key-value pairs that can easily be updated
and polled to determine the next best edge
to add to the MST.
Key realization: for any MST with directed
edges, each node is paired with exactly one of
its incoming edges (except for the start node).
Eager Prim’s
This can easily be seen on a directed MST
where you can have multiple edges leaving a
node, but at most one edge entering a node.
Let’s take a closer look…
Eager Prim’s
0
2
5
3
1
6
4
9
4
1
2 3
0
7
6
-2
6
5 3
Original undirected graph.
Eager Prim’s
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
Equivalent directed version.
Eager Prim’s
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
MST starting from node 0.
Eager Prim’s
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
Looking at the directed MST, you can see that
each node is paired with an incoming edge
except for the starting node.
Eager Prim’s
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
Looking at the directed MST, you can see that
each node is paired with an incoming edge
except for the starting node.
Eager Prim’s
A slight difference from the lazy version is
that instead of adding edges to the PQ as we
iterate over the edges of node, we’re going to
relax (update) the destination node’s most
promising incoming edge.
In the eager version of Prim’s we are trying
to determine which of a node’s incoming edges
we should select to include in the MST.
Eager Prim’s
A natural question to ask at this point is
how are we going to efficiently update and
retrieve these (node, edge) pairs?
One possible solution is to use an Indexed
Priority Queue (IPQ) which can efficiently
update and poll key-value pairs. This reduces
the overall time complexity from O(E*logE) to
O(E*logV) since there can only be V (node,
edge) pairs in the IPQ, making the update and
poll operations O(logV).
Indexed Priority Queue DS Video
<insert video clip>
Link to IPQ video the description
Eager Prim’s Algorithm
Start the algorithm on any node 's'. Mark s
as visited and relax all edges of s.
Maintain a min Indexed Priority Queue (IPQ) of
size V that sorts vertex-edge pairs (v, e) based
on the min edge cost of e. By default, all
vertices v have a best value of (v, ∅) in the IPQ.
While the IPQ is not empty and a MST has not been
formed, dequeue the next best (v, e) pair from the IPQ.
Mark node v as visited and add edge e to the MST.
Next, relax all edges of v while making sure not
to relax any edge pointing to a node which has
already been visited.
relaxing in this context refers to updating the entry for
node v in the IPQ from (v, eold) to (v, enew) if the new edge
enew from u -> v has a lower cost than eold.
0
2
5
3
1
6
4
9
4
1
2
0
7
-2
6
5 3
3
6
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
One thing to bear in mind is that while the
graph above represents an undirected graph,
the internal adjacency list representation
typically has each undirected edge stored as
two directed edges.
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
(node, edge) key-value
pairs in IPQ
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
node index -> (start node, end node, edge cost)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
The next cheapest (node, edge) pair
is 2 -> (0, 2, 0).
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
Ignore edges pointing to already-visited
nodes.
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (0, 5, 7)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
Edge (2, 5, 6) has a lower cost going to node
5 so update the IPQ with the new edge.
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
Edge (2, 5, 6) has a lower cost going to node
5 so update the IPQ with the new edge.
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (0, 1, 9)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (2, 5, 6)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (3, 6, 3)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5 3
9
-2
5
2
3
1
6
7 4
6
3
0
(node, edge) key-value
pairs in IPQ
2 -> (0, 2, 0)
5 -> (3, 5, 2)
3 -> (0, 3, 5)
1 -> (3, 1, -2)
6 -> (5, 6, 1)
4 -> (1, 4, 3)
0
2
5
3
1
6
4
9
4
1
2
0
7
-2
6
5 3
3
6
If we collapse the graph back into the
undirected edge view it becomes clear which
edges are included in the MST.
0
2
5
3
1
6
4
9
4
1
2
0
7
-2
6
5 3
3
6
The MST cost is:
0 + 5 + 2 + 1 + -2 + 3 = 9
n = … # Number of nodes in the graph.
ipq = … # IPQ data structure; stores (node index, edge object)
# pairs. The edge objects consist of {start node, end
# node, edge cost} tuples. The IPQ sorts (node index,
# edge object) pairs based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Eager Prim’s Pseudo Code
Let’s define a few variables we’ll need:
Eager Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
ipq = … # IPQ data structure; stores (node index, edge object)
# pairs. The edge objects consist of {start node, end
# node, edge cost} tuples. The IPQ sorts (node index,
# edge object) pairs based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Eager Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
ipq = … # IPQ data structure; stores (node index, edge object)
# pairs. The edge objects consist of {start node, end
# node, edge cost} tuples. The IPQ sorts (node index,
# edge object) pairs based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Eager Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
ipq = … # IPQ data structure; stores (node index, edge object)
# pairs. The edge objects consist of {start node, end
# node, edge cost} tuples. The IPQ sorts (node index,
# edge object) pairs based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
Graph edge density analysis
Y-axis: time required to find MST in milliseconds.
X-axis: Edge density percentage on graph with 5000 nodes.
10% 20% 30% 40% 50% 60% 70% 80% 90%
125ms
250ms
375ms
Adjacency List Adjacency Matrix
Eager Prim’s Pseudo Code
Let’s define a few variables we’ll need:
n = … # Number of nodes in the graph.
ipq = … # IPQ data structure; stores (node index, edge object)
# pairs. The edge objects consist of {start node, end
# node, edge cost} tuples. The IPQ sorts (node index,
# edge object) pairs based on min edge cost.
g = … # Graph representing an adjacency list of weighted edges.
# Each undirected edge is represented as two directed
# edges in g. For especially dense graphs, prefer using
# an adjacency matrix instead of an adjacency list to
# improve performance.
visited = [false, …, false] # visited[i] tracks whether node i
# has been visited; size n
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
Helper method
Helper method
function relaxEdgesAtNode(currentNodeIndex):
# Mark the current node as visited.
visited[currentNodeIndex] = true
# Get all the edges going outwards from the current node.
edges = g[currentNodeIndex]
for (edge : edges):
destNodeIndex = edge.to
# Skip edges pointing to already visited nodes.
if visited[destNodeIndex]:
continue
if !ipq.contains(destNodeIndex):
# Insert edge for the first time.
ipq.insert(destNodeIndex, edge)
else:
# Try and improve the cheapest edge at destNodeIndex with
# the current edge in the IPQ.
ipq.decreaseKey(destNodeIndex, edge)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
# s - the index of the starting node (0 ≤ s < n)
function eagerPrims(s = 0):
m = n - 1 # number of edges in MST
edgeCount, mstCost = 0, 0
mstEdges = [null, …, null] # size m
relaxEdgesAtNode(s)
while (!ipq.isEmpty() and edgeCount != m):
# Extract the next best (node index, edge object)
# pair from the IPQ
destNodeIndex, edge = ipq.dequeue()
mstEdges[edgeCount++] = edge
mstCost += edge.cost
relaxEdgesAtNode(destNodeIndex)
if edgeCount != m:
return (null, null) # No MST exists!
return (mstCost, mstEdges)
Next Video: eager Prim’s source code
Prim’s Minimum Spanning Tree
0
2
5
3
1
6
4
9
4
1
2
3
0
7
6
-2
6
5
3
9
-2
5
2
3
1
6
7 4
6
3
0
Looking at the directed MST, you can see that
each node is paired with an incoming edge
except for the starting node.
Prim’s MST
Source Code
William Fiset
Graph Theory
Video Series
graph theory algorithms, graph theory algorithms
graph theory algorithms, graph theory algorithms

graph theory algorithms, graph theory algorithms

  • 1.
  • 2.
    Graph Theory Intro &Overview William Fiset
  • 3.
    Brief introduction Graph theoryis the mathematical theory of the properties and applications of graphs (networks). The goal of this series is to gain an understanding of how to apply graph theory to real world applications.
  • 4.
    Brief introduction A graphtheory problem might be: Given the constraints above, how many different sets of clothing can I make by choosing an article from each category?
  • 5.
    Brief introduction The canonicalgraph theory example is a social network of friends. This enables interesting questions such as: how many friends does person X have? Or how many degrees of separation are there between person X and person Y?
  • 6.
  • 7.
    Undirected Graph An undirectedgraph is a graph in which edges have no orientation. The edge (u, v) is identical to the edge (v, u). - Wiki
  • 8.
    Undirected Graph An undirectedgraph is a graph in which edges have no orientation. The edge (u, v) is identical to the edge (v, u). - Wiki B A C D E F In the graph above, the nodes could represent cities and an edge could represent a bidirectional road.
  • 9.
    Directed Graph (Digraph) Adirected graph or digraph is a graph in which edges have orientations. For example, the edge (u, v) is the edge from node u to node v.
  • 10.
    Directed Graph (Digraph) A D C B F E Inthe graph above, the nodes could represent people and an edge (u, v) could represent that person u bought person v a gift. A directed graph or digraph is a graph in which edges have orientations. For example, the edge (u, v) is the edge from node u to node v.
  • 11.
    Weighted Graphs Many graphscan have edges that contain a certain weight to represent an arbitrary value such as cost, distance, quantity, etc… B A C D E F 4 4 8 2 3 1 11 2 9 NOTE: I will usually denote an edge of such a graph as a triplet (u, v, w) and specify whether the graph is directed or undirected.
  • 12.
  • 13.
    Trees! A tree isan undirected connected graph with no cycles. Equivalently, it is a connected graph with N nodes and N-1 edges.
  • 14.
    Rooted Trees! A rootedtree is a tree with a designated root node where every edge either points away from or towards the root node. When edges point away from the root the graph is called an arborescence (out-tree) and anti-arborescence (in-tree) otherwise. In-tree Out-tree Out-tree
  • 15.
    Directed Acyclic Graphs(DAGs) DAGs are directed graphs with no cycles. These graphs play an important role in representing structures with dependencies. Several efficient algorithms exist to operates on DAGs. Cool fact: All out-trees are DAGs but not all DAGs are out-trees.
  • 16.
    Bipartite Graph A bipartitegraph is one whose vertices can be split into two independent groups U, V such that every edge connects betweens U and V. Other definitions exist such as: The graph is two colourable or there is no odd length cycle.
  • 17.
    Complete Graphs A completegraph is one where there is a unique edge between every pair of nodes. A complete graph with n vertices is denoted as the graph Kn. K1 K2 K3 K4 K5 K6
  • 18.
  • 19.
    Adjacency Matrix 0 41 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D A C B D 4 9 1 3 6 11 4 1 2 6 5 -4 A adjacency matrix m is a very simple way to represent a graph. The idea is that the cell m[i][j] represents the edge weight of going from node i to node j. NOTE: It is often assumed that the edge of going from a node to itself has a cost of zero.
  • 20.
    Adjacency Matrix Pros Cons Spaceefficient for representing dense graphs Requires Θ(V²) space Edge weight lookup is O(1) Iterating over all edges takes Θ(V²) time Simplest graph representation
  • 21.
    Adjacency List An adjacencylist is a way to represent a graph as a map from nodes to lists of edges. A -> [(B,4),(C,1)] B -> [(C,6)] C -> [(A,4),(B,1),(D,2)] D -> [] A C B D 4 1 6 4 1 2
  • 22.
    A -> [(B,4),(C,1)] B-> [(C,6)] C -> [(A,4),(B,1),(D,2)] D -> [] Adjacency List A C B D 4 1 6 4 1 2 Node C can reach Node A with cost 4 Node B with cost 1 Node D with cost 2 An adjacency list is a way to represent a graph as a map from nodes to lists of edges.
  • 23.
    Adjacency List Pros Cons Spaceefficient for representing sparse graphs Less space efficient for denser graphs. Iterating over all edges is efficient Edge weight lookup is O(E) Slightly more complex graph representation
  • 24.
    Edge List This representationis seldomly used because of its lack of structure. However, it is conceptually simple and practical in a handful of algorithms. [(C,A,4), (A,C,1), (B,C,6), (A,B,4), (C,B,1), (C,D,2)] A C B D 4 1 6 4 1 2 An edge list is a way to represent a graph simply as an unordered list of edges. Assume the notation for any triplet (u,v,w) means: “the cost from node u to node v is w”
  • 25.
    Pros Cons Space efficientfor representing sparse graphs Less space efficient for denser graphs. Iterating over all edges is efficient Edge weight lookup is O(E) Very simple structure Edge List
  • 26.
    Next Video: GraphTheory Problems
  • 28.
  • 29.
  • 30.
  • 31.
    Common Graph TheoryProblems Is the graph directed or undirected? Are the edges of the graph weighted? Is the graph I will encounter likely to be sparse or dense with edges? Should I use an adjacency matrix, adjacency list, an edge list or other structure to represent the graph efficiently? For the upcoming problems ask yourself:
  • 32.
    Shortest path problem Algorithms:BFS (unweighted graph), Dijkstra’s, Bellman-Ford, Floyd-Warshall, A*, and many more. Given a weighted graph, find the shortest path of edges from node A to node B. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 11 11 4 Start End
  • 33.
    Algorithms: BFS (unweightedgraph), Dijkstra’s, Bellman-Ford, Floyd-Warshall, A*, and many more. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 11 11 4 Start End Shortest path problem Given a weighted graph, find the shortest path of edges from node A to node B.
  • 34.
    Connectivity Typical solution: Useunion find data structure or any search algorithm (e.g DFS). Does there exist a path between node A and node B?
  • 35.
    Connectivity Typical solution: Useunion find data structure or any search algorithm (e.g DFS). Does there exist a path between node A and node B?
  • 36.
    Negative cycles Algorithms: Bellman-Fordand Floyd-Warshall Does my weighted digraph have any negative cycles? If so, where? 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 3 Unaffected node Directly in negative cycle
  • 37.
    Negative cycles Algorithms: Bellman-Fordand Floyd-Warshall Does my weighted digraph have any negative cycles? If so, where? Unaffected node Directly in negative cycle 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 3
  • 38.
    Strongly Connected Components StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle. Algorithms: Tarjan’s and Kosaraju's algorithm
  • 39.
    Strongly Connected Components StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle. Algorithms: Tarjan’s and Kosaraju's algorithm
  • 40.
    Traveling Salesman Problem Algorithms:Held-Karp, branch and bound and many approximation algorithms "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city exactly once and returns to the origin city?” - Wiki A C B D 4 9 1 3 6 11 4 1 2 6 5 -4
  • 41.
    Traveling Salesman Problem Algorithms:Held-Karp, branch and bound and many approximation algorithms "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city exactly once and returns to the origin city?” - Wiki A C B D 4 9 1 3 6 11 4 1 2 6 5 -4 A C B D 9 3 1 -4
  • 42.
    Traveling Salesman Problem Algorithms:Held-Karp, branch and bound and many approximation algorithms The TSP problem is NP-Hard meaning it’s a very computationally challenging problem. This is unfortunate because the TSP has several very important applications. A C B D 4 9 1 3 6 11 4 1 2 6 5 -4 A C B D 9 3 1 -4
  • 43.
    Bridges A bridge /cut edge is any edge in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7
  • 44.
    Bridges A bridge /cut edge is any edge in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7 Bridges are important in graph theory because they often hint at weak points, bottlenecks or vulnerabilities in a graph.
  • 45.
    Articulation points An articulationpoint / cut vertex is any node in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7
  • 46.
    Articulation points An articulationpoint / cut vertex is any node in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7 Articulation points are important in graph theory because they often hint at weak points, bottlenecks or vulnerabilities in a graph.
  • 47.
    A E D F G B H I C J 5 2 9 1 2 1 5 7 4 6 1 0 8 4 4 2 1 1 Minimum SpanningTree (MST) Algorithms: Kruskal’s, Prim’s & Borůvka's algorithm A minimum spanning tree (MST) is a subset of the edges of a connected, edge-weighted graph that connects all the vertices together, without any cycles and with the minimum possible total edge weight. - Wiki
  • 48.
    Algorithms: Kruskal’s, Prim’s& Borůvka's algorithm A minimum spanning tree (MST) is a subset of the edges of a connected, edge-weighted graph that connects all the vertices together, without any cycles and with the minimum possible total edge weight. - Wiki A E D F G B H I C J 2 1 2 1 1 0 4 2 1 This MST has a total weight of 14. Note that MSTs on a graph are not always unique. Minimum Spanning Tree (MST)
  • 49.
    Algorithms: Kruskal’s, Prim’s& Borůvka's algorithm MSTs are seen in many applications including: Designing a least cost network, circuit design, transportation networks, and etc… A E D F G B H I C J 2 1 2 1 1 0 4 2 1 Minimum Spanning Tree (MST) This MST has a total weight of 14. Note that MSTs on a graph are not always unique.
  • 50.
    Network flow: maxflow Algorithms: Ford-Fulkerson, Edmonds-Karp & Dinic’s algorithm 1 2 1 8 4 2 1 3 4 5 6 3 3 7 1 4 3 8 Source Sink Q: With an infinite input source how much “flow” can we push through the network? Suppose the edges are roads with cars, pipes with water or hallways with packed with people. Flow represents the volume of water allowed to flow through the pipes, the number of cars the roads can sustain in traffic and the maximum amount of people that can navigate through the hallways.
  • 51.
    Next Video: DepthFirst Search
  • 53.
  • 54.
  • 55.
    Graph Theory: Depth FirstSearch William Fiset
  • 56.
    DFS overview The DepthFirst Search (DFS) is the most fundamental search algorithm used to explore nodes and edges of a graph. It runs with a time complexity of O(V+E) and is often used as a building block in other algorithms. By itself the DFS isn’t all that useful, but when augmented to perform other tasks such as count connected components, determine connectivity, or find bridges/articulation points then DFS really shines.
  • 57.
    As the namesuggests, a DFS plunges depth first into a graph without regard for which edge it takes next until it cannot go any further at which point it backtracks and continues. Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 58.
    Start DFS atnode 0 Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 59.
    Pick an edgeoutwards from node 0 Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 60.
    Once at 9pick an edge outwards from node 9 Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 61.
    Go to node8 Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 62.
    Pick an edgeoutwards from 8… Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
    Make sure youdon’t re-visit visited nodes! Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
    We haven’t finishedvisiting all the neighbours of 7 so continue DFS in another direction Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 76.
  • 77.
  • 78.
  • 79.
    Backtrack when adead end is reached. Basic DFS 7 3 11 10 8 6 9 1 2 5 4 12 0
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 111.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 112.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 113.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 114.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 115.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 116.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 117.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph visited = [false, …, false] # size n function dfs(at): if visited[at]: return visited[at] = true neighbours = graph[at] for next in neighbours: dfs(next) # Start DFS at node zero start_node = 0 dfs(start_node)
  • 118.
  • 119.
    Connected Components Sometimes agraph is split into multiple components. It’s useful to be able to identify and count these components.
  • 120.
    Connected Components Sometimes agraph is split into multiple components. It’s useful to be able to identify and count these components.
  • 121.
    Connected Components Assign aninteger value to each group to be able to tell them apart. 0 0 0 2 1 1 1 1 1 3 3 3 3 3 4 4 4 4
  • 122.
    We can usea DFS to identify components. First, make sure all the nodes are labeled from [0, n) where n is the number of nodes. 6 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2
  • 123.
    Algorithm: Start aDFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11
  • 124.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0
  • 125.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0
  • 126.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0
  • 127.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0
  • 128.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0
  • 129.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0
  • 130.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0
  • 131.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0
  • 132.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 133.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 134.
    6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 135.
    6 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 136.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 137.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0
  • 138.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0
  • 139.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0
  • 140.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0
  • 141.
    1 7 11 6 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 1 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0
  • 142.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0
  • 143.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1
  • 144.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1
  • 145.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1
  • 146.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1
  • 147.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1
  • 148.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1
  • 149.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1
  • 150.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1 1
  • 151.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1 1
  • 152.
    1 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 6 7 11 Algorithm: Starta DFS at every node (except if it’s already been visited) and mark all reachable nodes as being part of the same component. 0 0 0 0 0 1 1 1 1
  • 153.
    … and soon for the other components 6 7 11 1 5 17 16 12 4 0 8 14 13 15 9 10 3 2 0 0 0 0 0 1 1 1 1 2 2 2 3 3 3 3 3 4
  • 154.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 155.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 156.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 157.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 158.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 159.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 160.
    # Global orclass scope variables n = number of nodes in the graph g = adjacency list representing graph count = 0 components = empty integer array # size n visited = [false, …, false] # size n function findComponents(): for (i = 0; i < n; i++): if !visited[i]: count++ dfs(i) return (count, components) function dfs(at): visited[at] = true components[at] = count for (next : g[at]): if !visited[next]: dfs(next)
  • 161.
    What else canDFS do? We can augment the DFS algorithm to: • Compute a graph’s minimum spanning tree. • Detect and find cycles in a graph. • Check if a graph is bipartite. • Find strongly connected components. • Topologically sort the nodes of a graph. • Find bridges and articulation points. • Find augmenting paths in a flow network. • Generate mazes.
  • 162.
    Next Video: BreadthFirst Search
  • 163.
  • 164.
  • 165.
    Graph Theory: Breadth FirstSearch William Fiset
  • 166.
    BFS overview The BreadthFirst Search (BFS) is another fundamental search algorithm used to explore nodes and edges of a graph. It runs with a time complexity of O(V+E) and is often used as a building block in other algorithms. The BFS algorithm is particularly useful for one thing: finding the shortest path on unweighted graphs.
  • 167.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 168.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 169.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 170.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 171.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 172.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 173.
    7 3 11 0 8 6 9 1 2 5 4 12 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 174.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 175.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 176.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 177.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 178.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 179.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 180.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 181.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 182.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 183.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 184.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 185.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 186.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 187.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 188.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 189.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 190.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 12 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 191.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 12 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 192.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 193.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 194.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 A BFS startsat some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 195.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 196.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 197.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 198.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 199.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 200.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 201.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 202.
    7 3 11 0 8 6 9 1 2 5 4 12 10 0 9 7 11 10 8 6 3 1 5 12 2 4 A BFSstarts at some arbitrary node of a graph and explores the neighbour nodes first, before moving to the next level neighbours.
  • 203.
    Using a Queue TheBFS algorithm uses a queue data structure to track which node to visit next. Upon reaching a new node the algorithm adds it to the queue to visit it later. The queue data structure works just like a real world queue such as a waiting line at a restaurant. People can either enter the waiting line (enqueue) or get seated (dequeue). Queue Front Queue Back Dequeue Enqueue
  • 204.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 205.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 206.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 207.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 208.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 209.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 210.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 211.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 212.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 213.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 214.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 215.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 216.
    function solve(s): q =queue data structure with enqueue and dequeue q.enqueue(s) visited = [false, …, false] # size n visited[s] = true prev = [null, …, null] # size n while !q.isEmpty(): node = q.dequeue() neighbours = g.get(node) for(next : neighbours): if !visited[next]: q.enqueue(next) visited[next] = true prev[next] = node return prev
  • 217.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 218.
    # Global/class scopevariables n = number of nodes in the graph g = adjacency list representing unweighted graph # s = start node, e = end node, and 0 ≤ e,s < n function bfs(s, e): # Do a BFS starting at node s prev = solve(s) # Return reconstructed path from s -> e return reconstructPath(s, e, prev)
  • 219.
    function reconstructPath(s, e,prev): # Reconstruct path going backwards from e path = [] for(at = e; at != null; at = prev[at]): path.add(at) path.reverse() # If s and e are connected return the path if path[0] == s: return path return []
  • 220.
    function reconstructPath(s, e,prev): # Reconstruct path going backwards from e path = [] for(at = e; at != null; at = prev[at]): path.add(at) path.reverse() # If s and e are connected return the path if path[0] == s: return path return []
  • 221.
    function reconstructPath(s, e,prev): # Reconstruct path going backwards from e path = [] for(at = e; at != null; at = prev[at]): path.add(at) path.reverse() # If s and e are connected return the path if path[0] == s: return path return []
  • 222.
    function reconstructPath(s, e,prev): # Reconstruct path going backwards from e path = [] for(at = e; at != null; at = prev[at]): path.add(at) path.reverse() # If s and e are connected return the path if path[0] == s: return path return []
  • 224.
  • 225.
  • 226.
    BFS Shortest Path ona Grid William Fiset
  • 227.
    BFS Video Please watchBreadth First Search Algorithm to understand the basics of a BFS before proceeding. Link should be in the description below.
  • 228.
    Motivation Many problems ingraph theory can be represented using a grid. Grids are a form of implicit graph because we can determine a node’s neighbours based on our location within the grid.
  • 229.
    Motivation Many problems ingraph theory can be represented using a grid. Grids are a form of implicit graph because we can determine a node’s neighbours based on our location within the grid. A type of problem that involves finding a path through a grid is solving a maze:
  • 230.
    Motivation Many problems ingraph theory can be represented using a grid. Grids are a form of implicit graph because we can determine a node’s neighbours based on our location within the grid. Another example could be routing through obstacles (trees, rivers, rocks, etc) to get to a location:
  • 231.
    Graph Theory onGrids IMPORTANT: Assume the grid is unweighted and cells connect left, right, up and down. Empty Grid A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 232.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. First label the cells in the grid with numbers [0, n) where n = #rows x #columns 0 1 2 3 4 5 Empty Grid Graph Theory on Grids A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 233.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 234.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 235.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 236.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 237.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] 3 -> [1, 2, 5] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 238.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] 3 -> [1, 2, 5] 4 -> [2, 5] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 239.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 1 1 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] 3 -> [1, 2, 5] 4 -> [2, 5] 5 -> [3, 4] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 240.
    IMPORTANT: Assume thegrid is unweighted and cells connect left, right, up and down. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 1 1 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Graph Theory on Grids Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] 3 -> [1, 2, 5] 4 -> [2, 5] 5 -> [3, 4] A common approach to solving graph theory problems on grids is to first convert the grid to a familiar format such as an adjacency list/matrix.
  • 241.
    However, transformations betweengraph representations can usually be avoided due to the structure of a grid. Once we have an adjacency list/matrix we can run whatever specialized graph algorithm to solve our problem such as: shortest path, connected components, etc… Graph Theory on Grids
  • 242.
    Direction Vectors Due tothe structure of a grid, if we are at the red ball in the middle we know we can move left, right, up and down to reach adjacent cells:
  • 243.
    Direction Vectors Mathematically, ifthe red ball is at the row- column coordinate (r, c) we can add the row vectors [-1, 0], [1, 0], [0, 1], and [0, -1] to reach adjacent cells. (r, c) (r-1, c) (r+1, c) (r, c+1) (r, c-1)
  • 244.
    Direction Vectors If theproblem you are trying to solve allows moving diagonally then you can also include the row vectors: [-1, -1], [-1, 1], [1, 1], [1, -1] (r, c) (r-1, c) (r+1, c) (r, c+1) (r, c-1) (r-1, c+1) (r+1, c+1) (r+1, c-1) (r-1, c-1)
  • 245.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 246.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 247.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 248.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 249.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 250.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 251.
    Direction Vectors This makesit very easy to access neighbouring cells from the current row-column position: # Define the direction vectors for # north, south, east and west. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1] for(i = 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip invalid cells. Assume R and # C for the number of rows and columns if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue #(rr, cc) is a neighbouring cell of (r, c)
  • 252.
    Dungeon Problem Statement Youare trapped in a 2D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west. You cannot move diagonally and the maze is surrounded by solid rock on all sides. Is an escape possible? If yes, how long will it take? This is an easier version of the “Dungeon Master” problem on Kattis: open.kattis.com/problems/dungeon. Link in description.
  • 253.
    Dungeon Problem Statement Thedungeon has a size of R x C and you start at cell ’S’ and there’s an exit at cell ‘E’. A cell full of rock is indicated by a ’#’ and empty cells are represented by a ’.’ R C S . . # . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # .
  • 254.
    Dungeon Problem Statement S. . # . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # . R C The dungeon has a size of R x C and you start at cell ’S’ and there’s an exit at cell ‘E’. A cell full of rock is indicated by a ’#’ and empty cells are represented by a ’.’
  • 255.
    S . .# . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4
  • 256.
    (0, 0) (0,0) .. # . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 Start at the start node coordinate by adding (sr, sc) to the queue.
  • 257.
    (0,0) (0,1) .# . . . (1,0) # . . . # . . # . . . . . . . # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 0)
  • 258.
    (0,0) (0,1) (0,2)# . . . (1,0) # . . . # . (2,0) # . . . . . . . # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0)
  • 259.
    (0,0) (0,1) (0,2)# . . . (1,0) # (1,2) . . # . (2,0) # . . . . . (3,0) . # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0)
  • 260.
    (0,0) (0,1) (0,2)# . . . (1,0) # (1,2) (1,3) . # . (2,0) # (2,2) . . . . (3,0) (3,1) # # . . . # . # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1)
  • 261.
    (0,0) (0,1) (0,2)# . . . (1,0) # (1,2) (1,3) (1,4) # . (2,0) # (2,2) (2,3) . . . (3,0) (3,1) # # . . . # (4,1) # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1)
  • 262.
    (0,0) (0,1) (0,2)# (0,4) . . (1,0) # (1,2) (1,3) (1,4) # . (2,0) # (2,2) (2,3) (2,4) . . (3,0) (3,1) # # . . . # (4,1) # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1) (2, 4) (0, 4)
  • 263.
    (0,0) (0,1) (0,2)# (0,4) (0,5) . (1,0) # (1,2) (1,3) (1,4) # . (2,0) # (2,2) (2,3) (2,4) (2,5) . (3,0) (3,1) # # (3,4) . . # (4,1) # E . # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1) (2, 4) (0, 4) 2, 5) (3, 4) (0, 5)
  • 264.
    (0,0) (0,1) (0,2)# (0,4) (0,5) (0,6) (1,0) # (1,2) (1,3) (1,4) # . (2,0) # (2,2) (2,3) (2,4) (2,5) (2,6) (3,0) (3,1) # # (3,4) (3,5) . # (4,1) # E (4,4) # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1) (2, 4) (0, 4) 2, 5) (3, 4) (0, 5) (3, 5) (4, 4) (0, 6) (2, 6)
  • 265.
    (0,0) (0,1) (0,2)# (0,4) (0,5) (0,6) (1,0) # (1,2) (1,3) (1,4) # (1,6) (2,0) # (2,2) (2,3) (2,4) (2,5) (2,6) (3,0) (3,1) # # (3,4) (3,5) (3,6) # (4,1) # (4,3) (4,4) # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1) (2, 4) (0, 4) 2, 5) (3, 4) (0, 5) (3, 5) (4, 4) (0, 6) (2, 6) (3,6) (1, 6) (4, 3)
  • 266.
    (0,0) (0,1) (0,2)# (0,4) (0,5) (0,6) (1,0) # (1,2) (1,3) (1,4) # (1,6) (2,0) # (2,2) (2,3) (2,4) (2,5) (2,6) (3,0) (3,1) # # (3,4) (3,5) (3,6) # (4,1) # (4,3) (4,4) # . 0 1 2 3 4 5 6 0 1 2 3 4 (0, 1) (1, 0) (0, 2) (0, 0) (2, 0) (1, 2) (3, 0) (1, 3) (2, 2) (3, 1) (1, 4) (2, 3) (4, 1) (2, 4) (0, 4) 2, 5) (3, 4) (0, 5) (3, 5) (4, 4) (0, 6) (2, 6) (3,6) (1, 6) (4, 3) We have reached the end, and if we had a 2D prev matrix we could regenerate the path by retracing our steps.
  • 267.
    Alternative State representation Sofar we have been storing the next x-y position in the queue as an (x, y) pair. This works well but requires either an array or an object wrapper to store the coordinate values. In practice, this requires a lot of packing and unpacking of values to and from the queue. Let’s take a look at an alternative approach which also scales well in higher dimensions and (IMHO) requires less setup effort…
  • 268.
    Alternative State representation Analternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions.
  • 269.
    Alternative State representation x1y1 z1 Enqueuing (x1, y1, z1) x queue y queue z queue An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions.
  • 270.
    Alternative State representation x1y1 z1 x queue y queue z queue x2 y2 z2 Enqueuing (x2, y2, z2) An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions.
  • 271.
    Alternative State representation x1y1 z1 x queue y queue z queue x2 y2 z2 Enqueuing (x3, y3, z3) x3 y3 z3 An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions.
  • 272.
    Alternative State representation x1y1 z1 x queue y queue z queue x2 y2 z2 Dequeuing (x3, y3, z3) An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions. x3 y3 z3
  • 273.
    Alternative State representation x1y1 z1 x queue y queue z queue Dequeuing (x2, y2, z2) An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions. x2 y2 z2
  • 274.
    Alternative State representation x1y1 z1 x queue y queue z queue An alternative approach is to use one queue for each dimension, so in a 3D grid you would have one queue for each of the x, y, and z dimensions.
  • 275.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 276.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 277.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 278.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 279.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 280.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 281.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 282.
    # Global/class scopevariables R, C = … # R = number of rows, C = number of columns m = … # Input character matrix of size R x C sr, sc = … # ’S’ symbol row and column values rq, cq = … # Empty Row Queue (RQ) and Column Queue (CQ) # Variables used to track the number of steps taken. move_count = 0 nodes_left_in_layer = 1 nodes_in_next_layer = 0 # Variable used to track whether the ‘E’ character # ever gets reached during the BFS. reached_end = false # R x C matrix of false values used to track whether # the node at position (i, j) has been visited. visited = … # North, south, east, west direction vectors. dr = [-1, +1, 0, 0] dc = [ 0, 0, +1, -1]
  • 283.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 284.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 285.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 286.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 287.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 288.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 289.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 290.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 291.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 292.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 293.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 294.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 295.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 296.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 297.
    function explore_neighbours(r, c): for(i= 0; i < 4; i++): rr = r + dr[i] cc = c + dc[i] # Skip out of bounds locations if rr < 0 or cc < 0: continue if rr >= R or cc >= C: continue # Skip visited locations or blocked cells if visited[rr][cc]: continue if m[rr][cc] == ‘#’: continue rq.enqueue(rr) cq.enqueue(cc) visited[rr][cc] = true nodes_in_next_layer++
  • 298.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 299.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 300.
    function solve(): rq.enqueue(sr) cq.enqueue(sc) visited[sr][sc] =true while rq.size() > 0: # or cq.size() > 0 r = rq.dequeue() c = cq.dequeue() if m[r][c] == ‘E’: reached_end = true break explore_neighbours(r, c) nodes_left_in_layer—- if nodes_left_in_layer == 0: nodes_left_in_layer = nodes_in_next_layer nodes_in_next_layer = 0 move_count++ if reached_end: return move_count return -1
  • 301.
    Summary Representing a gridas an adjacency list and adjacency matrix. 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 1 1 0 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Empty Grid Adjacency Matrix: Adjacency List: 0 -> [1, 2] 1 -> [0, 3] 2 -> [0, 3, 4] 3 -> [1, 2, 5] 4 -> [2, 5] 5 -> [3, 4]
  • 302.
    Summary Using direction vectorsto visit neighbouring cells. (r, c) (r-1, c) (r+1, c) (r, c+1) (r, c-1)
  • 303.
    Summary Explored an alternativeway to represent multi dimensional coordinates using multiple queues. x1 y1 z1 x queue y queue z queue x2 y2 z2 Dequeuing (x3, y3, z3) x3 y3 z3
  • 304.
    Summary How to useBFS on a grid to find the shortest path between two cells. S . . # . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # . R C
  • 306.
    S . .# . . . . # . . . # . . # . . . . . . . # # . . . # . # E . # . Breadth First Search Shortest Path on a Grid
  • 307.
  • 308.
  • 309.
    Many real worldsituations can be modelled as a graph with directed edges where some events must occur before others. • School class prerequisites • Program dependencies • Event scheduling • Assembly instructions • Etc…
  • 310.
    Class B Class E ClassF Class A Class C Class D Class H Class I Class J Suppose you’re a student at university X and you want to take Class H, then you must take classes A, B, D and E as prerequisites. In this sense there is an ordering on the nodes of the graph.
  • 311.
    Class B Class E ClassF Class A Class C Class D Class H Class I Class J Suppose you’re a student at university X and you want to take Class H, then you must take classes A, B, D and E as prerequisites. In this sense there is an ordering on the nodes of the graph.
  • 312.
    Class B Class E ClassF Class A Class C Class D Class H Class I Class J Suppose you’re a student at university X and you want to take Class H, then you must take classes A, B, D and E as prerequisites. In this sense there is an ordering on the nodes of the graph.
  • 313.
    Class B Class E ClassF Class A Class C Class D Class H Class I Class J Suppose you’re a student at university X and you want to take Class H, then you must take classes A, B, D and E as prerequisites. In this sense there is an ordering on the nodes of the graph.
  • 314.
    Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 315.
    A B C D E F G H I J K Want to build programJ Another canonical example where an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 316.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 317.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 318.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 319.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 320.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 321.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 322.
    A B C D E F G H I J K Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 323.
    H I J K A B C D E F G Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 324.
    H I J K A B C D E F G Another canonical examplewhere an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 325.
    H I J K A B C D E F G Not a dependency Anothercanonical example where an ordering on the nodes of the graph matters is for program build dependencies. A program cannot be built unless its dependencies are first built.
  • 326.
    H I J K A CB D F E G A topological ordering is an ordering of the nodes in a directed graph where for each directed edge from node A to node B, node A appears before node B in the ordering. NOTE: Topological orderings are NOT unique. The topological sort algorithm can find a topological ordering in O(V+E) time!
  • 327.
    Directed Acyclic Graphs(DAG) Notevery graph can have a topological ordering. A graph which contains a cycle cannot have a valid ordering: 1 0 2 5 4 3
  • 328.
    Directed Acyclic Graphs(DAG) 1 0 2 5 4 3 Notevery graph can have a topological ordering. A graph which contains a cycle cannot have a valid ordering:
  • 329.
    Directed Acyclic Graphs(DAG) Theonly type of graph which has a valid topological ordering is a Directed Acyclic Graph (DAG). These are graphs with directed edges and no cycles.
  • 330.
    Directed Acyclic Graphs(DAG) Q:How do I verify that my graph does not contain a directed cycle? A: One method is to use Tarjan’s strongly connected component algorithm which can be used to find these cycles. The only type of graph which has a valid topological ordering is a Directed Acyclic Graph (DAG). These are graphs with directed edges and no cycles.
  • 331.
    A By definition, allrooted trees have a topological ordering since they do not contain any cycles. Directed Acyclic Graphs(DAG) B C D G F I E H J K
  • 332.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 333.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 334.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 335.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 336.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 337.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 338.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 339.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 340.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B E By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 341.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B E D By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 342.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B E D C By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 343.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B E D C A By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 344.
    A Directed Acyclic Graphs(DAG) BC D G F I E H J K I F J K H G B E D C A Topological ordering from left to right: By definition, all rooted trees have a topological ordering since they do not contain any cycles.
  • 345.
    Pick an unvisitednode Beginning with the selected node, do a Depth First Search (DFS) exploring only unvisited nodes. On the recursive callback of the DFS, add the current node to the topological ordering in reverse order. Topological Sort Algorithm
  • 346.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Topological Sort Algorithm
  • 347.
    Topological Sort Algorithm A I E C F B D G J H K L M DFSrecursion call stack: Topological ordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Node H
  • 348.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Node H Topological Sort Algorithm
  • 349.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Node H Node J Topological Sort Algorithm
  • 350.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Node H Node J Topological Sort Algorithm
  • 351.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Node H Node J Node M Topological Sort Algorithm
  • 352.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ M Node H Node J Topological Sort Algorithm
  • 353.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ M Node H Node J Topological Sort Algorithm
  • 354.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ M Node H Node J Node L Topological Sort Algorithm
  • 355.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ L M Node H Node J Topological Sort Algorithm
  • 356.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ J L M Node H Topological Sort Algorithm
  • 357.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ J L M Node H Topological Sort Algorithm
  • 358.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ J L M Node H Node I Topological Sort Algorithm
  • 359.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ J L M Node H Node I Topological Sort Algorithm
  • 360.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ J L M Node H Node I Topological Sort Algorithm
  • 361.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ I J L M Node H Topological Sort Algorithm
  • 362.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Topological Sort Algorithm
  • 363.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Topological Sort Algorithm
  • 364.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Topological Sort Algorithm
  • 365.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Topological Sort Algorithm
  • 366.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Topological Sort Algorithm
  • 367.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Topological Sort Algorithm
  • 368.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Topological Sort Algorithm
  • 369.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Topological Sort Algorithm
  • 370.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Node G Topological Sort Algorithm
  • 371.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Node G Topological Sort Algorithm
  • 372.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H I J L M Node E Node A Node D Node G Topological Sort Algorithm
  • 373.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ G H I J L M Node E Node A Node D Topological Sort Algorithm
  • 374.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ D G H I J L M Node E Node A Topological Sort Algorithm
  • 375.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Topological Sort Algorithm
  • 376.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Topological Sort Algorithm
  • 377.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Topological Sort Algorithm
  • 378.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Node F Topological Sort Algorithm
  • 379.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Node F Topological Sort Algorithm
  • 380.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Node F Node K Topological Sort Algorithm
  • 381.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Node F Node K Topological Sort Algorithm
  • 382.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A D G H I J L M Node E Node F Node K Topological Sort Algorithm
  • 383.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ K A D G H I J L M Node E Node F Topological Sort Algorithm
  • 384.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ K A D G H I J L M Node E Node F Topological Sort Algorithm
  • 385.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ K A D G H I J L M Node E Node F Topological Sort Algorithm
  • 386.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ F K A D G H I J L M Node E Topological Sort Algorithm
  • 387.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Topological Sort Algorithm
  • 388.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Node C Topological Sort Algorithm
  • 389.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Node C Topological Sort Algorithm
  • 390.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Node C Node B Topological Sort Algorithm
  • 391.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Node C Node B Topological Sort Algorithm
  • 392.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ E F K A D G H I J L M Node C Node B Topological Sort Algorithm
  • 393.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ _ B E F K A D G H I J L M Node C Topological Sort Algorithm
  • 394.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ B E F K A D G H I J L M Node C Topological Sort Algorithm
  • 395.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ B E F K A D G H I J L M Node C Topological Sort Algorithm
  • 396.
    A I E C F B D G J H K L M DFS recursion call stack: Topologicalordering: _ _ _ _ _ _ _ _ _ _ _ _ _ C B E F K A D G H I J L M Topological Sort Algorithm
  • 397.
    # Assumption: graphis stored as adjacency list function topsort(graph): N = graph.numberOfNodes() V = [false,…,false] # Length N ordering = [0,…,0] # Length N i = N - 1 # Index for ordering array for(at = 0; at < N; at++): if V[at] == false: visitedNodes = [] dfs(at, V, visitedNodes, graph) for nodeId in visitedNodes: ordering[i] = nodeId i = i - 1 return ordering Topsort pseudocode
  • 398.
    # Execute DepthFirst Search (DFS) function dfs(at, V, visitedNodes, graph): V[at] = true edges = graph.getEdgesOutFromNode(at) for edge in edges: if V[edge.to] == false: dfs(edge.to, V, visitedNodes, graph) visitedNodes.add(at) Topsort pseudocode
  • 399.
    # Assumption: graphis stored as adjacency list function topsort(graph): N = graph.numberOfNodes() V = [false,…,false] # Length N ordering = [0,…,0] # Length N i = N - 1 # Index for ordering array for(at = 0; at < N; at++): if V[at] == false: visitedNodes = [] dfs(at, V, visitedNodes, graph) for nodeId in visitedNodes: ordering[i] = nodeId i = i - 1 return ordering Topsort pseudocode
  • 400.
    # Assumption: graphis stored as adjacency list function topsort(graph): N = graph.numberOfNodes() V = [false,…,false] # Length N ordering = [0,…,0] # Length N i = N - 1 # Index for ordering array for(at = 0; at < N; at++): if V[at] == false: visitedNodes = [] dfs(at, V, visitedNodes, graph) for nodeId in visitedNodes: ordering[i] = nodeId i = i - 1 return ordering Topsort pseudocode
  • 401.
    # Assumption: graphis stored as adjacency list function topsort(graph): N = graph.numberOfNodes() V = [false,…,false] # Length N ordering = [0,…,0] # Length N i = N - 1 # Index for ordering array for(at = 0; at < N; at++): if V[at] == false: i = dfs(i, at, V, ordering, graph) return ordering Topsort Optimization
  • 402.
    # Execute DepthFirst Search (DFS) function dfs(i, at, V, ordering, graph): V[at] = true edges = graph.getEdgesOutFromNode(at) for edge in edges: if V[edge.to] == false: i = dfs(i, edge.to, V, ordering, graph) ordering[i] = at return i - 1 Topsort Optimization
  • 403.
    Source Code Link Implementationsource code can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 405.
  • 406.
  • 407.
    Kahn’s Algorithm William Fiset An intuitivetopological sort algorithm e c b a f d
  • 408.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 409.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 410.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 411.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 412.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 413.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 414.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 415.
    How to getdressed school shoes socks underwear pants shirt hoodie One (of many) possible Topological Orderings:
  • 416.
    Many real worldsituations can be modeled as a graph with directed edges where some events must occur before others, for instance: • Program build dependencies • College class prerequisites • Event scheduling • Assembly instructions • Etc… Motivation
  • 417.
    H I J K A CB D F E G A topological ordering is an ordering of the nodes in a directed graph where for each directed edge from node A to node B, node A appears before node B in the ordering. NOTE: Topological orderings are NOT unique. Kahn’s algorithm is a simple topological sort algorithm can find a topological ordering in O(V+E) time!
  • 418.
    Which graphs havetopological sorts? Only certain types of graphs have a topological orderings. These are Directed Acyclic Graphs (DAGs). "A directed acyclic graph is a finite directed graph with no directed cycles." - Wiki
  • 419.
    Which of theseare Directed Acyclic Graphs (DAGs)? (1) (2) (3) (4) (5) (6)
  • 420.
    Which of theseare Directed Acyclic Graphs (DAGs)? Edges must be directed (1) (2) (3) (4) (5) (6)
  • 421.
    Which graphs havetopological sorts? Try and imagine what happens if you try and find the topological ordering of a graph that contains a cycle: 4 1 0 5 3 2
  • 422.
    Which graphs havetopological sorts? First you would pick node 4, the only node without any dependencies 4 1 0 5 3 2 4,
  • 423.
    Which graphs havetopological sorts? Then, you could process node 1, which no longer depends on node 4 4 1 0 5 3 2 4, 1
  • 424.
    Which graphs havetopological sorts? Followed by node 0, which no longer depends on node 1 4 1 0 5 3 2 4, 1, 0
  • 425.
    Which graphs havetopological sorts? Then you get stuck in a cycle. Every node depends on another node 😱 4 1 0 5 3 2 4, 1, 0, ???
  • 426.
    Kahn’s Algorithm Intuition Theintuition behind Kahn’s algorithm is to repeatedly remove nodes without any dependencies from the graph and add them to the topological ordering. As nodes without dependencies (and their outgoing edges) are removed from the graph, new nodes without dependencies should become free. We repeat removing nodes without dependencies from the graph until all nodes are processed, or a cycle is discovered.
  • 427.
  • 428.
    0 5 1 3 4 Topological Ordering: _ __ _ _ _ 2 Node 2 is the only node without dependencies
  • 429.
    0 5 1 3 4 Topological Ordering: 2 __ _ _ _ 2 Add node 2 to the topological ordering, and remove it from the graph.
  • 430.
    0 5 1 3 4 Topological Ordering: 2 __ _ _ _ 2 Node 0 and node 4 don’t have any dependencies. Select either to be added to the topological ordering.
  • 431.
    0 5 1 3 4 Topological Ordering: 2 0_ _ _ _ 2 Add node 0 to the topological ordering, and remove it from the graph.
  • 432.
    0 5 1 3 4 Topological Ordering: 2 0_ _ _ _ 2 Node 4 is the only node without dependencies
  • 433.
    0 5 1 3 4 Topological Ordering: 2 04 _ _ _ 2 Add node 4 to the topological ordering, and remove it from the graph.
  • 434.
    0 5 1 3 4 Topological Ordering: 2 04 _ _ _ 2 Node 3 and node 5 don’t have any dependencies. Select either to be added to the topological ordering.
  • 435.
    0 5 1 3 4 Topological Ordering: 2 04 3 _ _ 2 Add node 3 to the topological ordering, and remove it from the graph.
  • 436.
    0 5 1 3 4 Topological Ordering: 2 04 3 _ _ 2 Node 5 is the only node without dependencies
  • 437.
    0 5 1 3 4 Topological Ordering: 2 04 3 5 _ 2 Add node 5 to the topological ordering, and remove it from the graph.
  • 438.
    0 5 1 3 4 Topological Ordering: 2 04 3 5 _ 2 Finally, only node 1 remains
  • 439.
  • 440.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 0 1 23 4 5 6 7 8 9 10 11 12 13 _ _ _ _ _ _ _ _ _ _ _ _ _ _ Topological Ordering:
  • 441.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 0 1 23 4 5 6 7 8 9 10 11 12 13 _ _ _ _ _ _ _ _ _ _ _ _ _ _ Begin by counting the incoming degree of each node Topological Ordering:
  • 442.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 0 1 23 4 5 6 7 8 9 10 11 12 13 3 _ _ _ _ _ _ _ _ _ _ _ _ _ _ For example, node 6 has an incoming degree of 3 Topological Ordering:
  • 443.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 0 1 23 4 5 6 7 8 9 10 11 12 13 3 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ And node 7 has an incoming degree of 1 Topological Ordering:
  • 444.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 0 1 23 4 5 6 7 8 9 10 11 12 13 0 1 2 1 3 1 3 1 2 0 1 1 2 0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ Nodes by incoming degree count: Topological Ordering:
  • 445.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 1 2 1 3 1 3 1 2 0 1 1 2 0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ Maintain a queue of all nodes with no incoming edges Topological Ordering:
  • 446.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 0 node 9 node13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 1 3 1 3 1 2 0 1 1 2 0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ Add all nodes with a degree of 0 (no incoming edges) to the queue. These are all the nodes in the graph with no dependencies. Topological Ordering:
  • 447.
    2 12 9 3 10 6 11 4 7 1 8 5 13 Queue node 0 node 9 node13 0 _ _ _ _ _ _ _ _ _ _ _ _ _ Remove node 0 from the front of the queue, and add it to the topological ordering. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 1 3 1 3 1 2 0 1 1 2 0 0 Topological Ordering:
  • 448.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 9 node 13 01 2 3 4 5 6 7 8 9 10 11 12 13 0 1 1 0 3 1 2 1 2 0 1 1 2 0 0 _ _ _ _ _ _ _ _ _ _ _ _ _ Remove node 0 from the graph and decrease the degree of all affected nodes. Topological Ordering:
  • 449.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 9 node 13 01 2 3 4 5 6 7 8 9 10 11 12 13 0 1 1 0 3 1 2 1 2 0 1 1 2 0 0 _ _ _ _ _ _ _ _ _ _ _ _ _ Add any new nodes with an incoming degree of 0 to the queue. node 3 Topological Ordering:
  • 450.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 9 node 13 01 2 3 4 5 6 7 8 9 10 11 12 13 0 1 1 0 3 1 2 1 2 0 1 1 2 0 Topological Ordering: 0 9 _ _ _ _ _ _ _ _ _ _ _ _ node 3 Remove node 9 from the front of the queue, and add it to the topological ordering.
  • 451.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 13 0 12 3 4 5 6 7 8 9 10 11 12 13 0 1 0 0 3 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 _ _ _ _ _ _ _ _ _ _ _ _ node 3 Remove node 9 from the graph and decrease the degree of all affected nodes.
  • 452.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 13 0 12 3 4 5 6 7 8 9 10 11 12 13 0 1 0 0 3 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 _ _ _ _ _ _ _ _ _ _ _ _ node 3 Add any new nodes with an incoming degree of 0 to the queue. node 2 node 10
  • 453.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue node 13 0 12 3 4 5 6 7 8 9 10 11 12 13 0 1 0 0 3 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 _ _ _ _ _ _ _ _ _ _ _ node 3 node 2 node 10 Remove node 13 from the front of the queue, and add it to the topological ordering.
  • 454.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 1 0 0 3 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 _ _ _ _ _ _ _ _ _ _ _ node 3 node 2 node 10 Remove node 13 from the graph and decrease the degree of all affected nodes. Node 13 has no outgoing edges, so no node degrees are updated (and hence no new nodes are added to the queue either).
  • 455.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 1 0 0 3 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 _ _ _ _ _ _ _ _ _ _ node 3 node 2 node 10 Remove node 3 from the front of the queue, and add it to the topological ordering.
  • 456.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 _ _ _ _ _ _ _ _ _ _ node 2 node 10 Remove node 3 from the graph and decrease the degree of all affected nodes.
  • 457.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 _ _ _ _ _ _ _ _ _ _ node 2 node 10 Add any new nodes with an incoming degree of 0 to the queue. node 1
  • 458.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 _ _ _ _ _ _ _ _ _ _ node 2 node 10 Let the animation play… node 1
  • 459.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 2 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 _ _ _ _ _ _ _ _ _ node 2 node 10 node 1
  • 460.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 1 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 _ _ _ _ _ _ _ _ _ node 10 node 1
  • 461.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 1 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 _ _ _ _ _ _ _ _ node 10 node 1
  • 462.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 0 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 _ _ _ _ _ _ _ _ node 1
  • 463.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 0 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 _ _ _ _ _ _ _ _ node 1 node 6
  • 464.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 2 1 0 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 1 _ _ _ _ _ _ _ node 1 node 6
  • 465.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 1 1 0 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 1 _ _ _ _ _ _ _ node 6
  • 466.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 1 1 0 1 2 0 0 1 2 0 Topological Ordering: 0 9 13 3 2 10 1 6 _ _ _ _ _ _ node 6
  • 467.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 1 1 0 0 2 0 0 0 2 0 Topological Ordering: 0 9 13 3 2 10 1 6 _ _ _ _ _ _
  • 468.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 1 1 0 0 2 0 0 0 2 0 Topological Ordering: 0 9 13 3 2 10 1 6 _ _ _ _ _ _ node 7 node 11
  • 469.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 1 1 0 0 2 0 0 0 2 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 _ _ _ _ _ node 7 node 11
  • 470.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 1 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 _ _ _ _ _ node 11
  • 471.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 1 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 _ _ _ _ _ node 11 node 4
  • 472.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 1 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 _ _ _ _ node 11 node 4
  • 473.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 _ _ _ _ node 4
  • 474.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 _ _ _ _ node 4 node 12
  • 475.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 1 0 0 2 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 _ _ _ node 4 node 12
  • 476.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 1 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 _ _ _ node 12
  • 477.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 1 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 _ _ _ node 12 node 5
  • 478.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 1 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 _ _ node 12 node 5
  • 479.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 _ _ node 5
  • 480.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 _ _ node 5 node 8
  • 481.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 5 _ node 5 node 8
  • 482.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 5 _ node 8
  • 483.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 5 8 node 8
  • 484.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 5 8
  • 485.
    2 12 0 9 3 10 6 11 4 7 1 8 5 13 Queue 0 1 23 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Topological Ordering: 0 9 13 3 2 10 1 6 7 11 4 12 5 8
  • 486.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 487.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 488.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 489.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 490.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 491.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 492.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 493.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 494.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 495.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 496.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 497.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 498.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 499.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 500.
    Topological Ordering: 0 913 3 2 10 1 6 7 11 4 12 5 8 2 12 0 9 3 10 6 11 4 7 1 8 5 13
  • 501.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 502.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 503.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 504.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 505.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 506.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 507.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 508.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 509.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 510.
    # `g` isa directed acyclic graph represented as an adjacency list. function FindTopologicalOrdering(g): n = g.size() in_degree = [0,0,…,0,0] # size n for (i = 0; i < n; i++): for (to in g[i]): in_degree[to] = in_degree[to] + 1 # `q` always contains the set nodes with no incoming edges. q = … # empty integer queue data structure for (i = 0; i < n; i++): if (in_degree[i] == 0): q.enqueue(i) index = 0 order = [0,0,…0,0] # size n while (!q.isEmpty()): at = q.dequeue() order[index++] = at for (to in g[at]): in_degree[to] = in_degree[to] - 1 if in_degree[to] == 0: q.enqueue(to) if index != n: return null # Oops, graph contains a cycle return order
  • 511.
    Source Code Link Implementationsource code can be found at the following link: github.com/williamfiset/algorithms Link in the description
  • 512.
  • 513.
  • 514.
    Shortest and longest pathson DAGs William Fiset
  • 515.
    Directed Acyclic Graph(DAG) Recall that a Directed Acyclic Graph (DAG) is a graph with directed edges and no cycles. By definition this means all trees are automatically DAGs since they do not contain cycles.
  • 516.
    Directed Acyclic Graph(DAG) Recall that a Directed Acyclic Graph (DAG) is a graph with directed edges and no cycles. By definition this means all trees are automatically DAGs since they do not contain cycles. Q: Is this graph a DAG?
  • 517.
    Directed Acyclic Graph(DAG) Recall that a Directed Acyclic Graph (DAG) is a graph with directed edges and no cycles. By definition this means all trees are automatically DAGs since they do not contain cycles. Q: Is this graph a DAG? A: Yes!
  • 518.
    Directed Acyclic Graph(DAG) Recall that a Directed Acyclic Graph (DAG) is a graph with directed edges and no cycles. By definition this means all trees are automatically DAGs since they do not contain cycles. Q: Is this graph a DAG?
  • 519.
    Directed Acyclic Graph(DAG) Recall that a Directed Acyclic Graph (DAG) is a graph with directed edges and no cycles. By definition this means all trees are automatically DAGs since they do not contain cycles. Q: Is this graph a DAG? A: No, the structure may be a tree, but it does not have directed edges.
  • 520.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially.
  • 521.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 11 11 4
  • 522.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 523.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 524.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 ∞ ∞ ∞ ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 525.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 ∞ ∞ ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 526.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 ∞ ∞ ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 527.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 ∞ 14 ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 528.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 529.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 530.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 531.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ ∞ ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 532.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ 17 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 533.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 ∞ 17 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 534.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 12 17 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 535.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 14 12 9 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 536.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 537.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 ∞ A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 538.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 539.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 540.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 541.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 542.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 543.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 544.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 545.
    SSSP on DAG TheSingle Source Shortest Path (SSSP) problem can be solved efficiently on a DAG in O(V+E) time. This is due to the fact that the nodes can be ordered in a topological ordering via topsort and processed sequentially. A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 0 3 6 7 3 12 9 11 A B C D E F G H 11 11 4 Arbitrary topological order: A, B, C, D, G, E, F, H
  • 546.
    Longest path onDAG What about the longest path? On a general graph this problem is NP-Hard, but on a DAG this problem is solvable in O(V+E)!
  • 547.
    The trick isto multiply all edge values by -1 then find the shortest path and then multiply the edge values by -1 again! Longest path on DAG What about the longest path? On a general graph this problem is NP-Hard, but on a DAG this problem is solvable in O(V+E)!
  • 548.
    A B C D E F G H 3 6 8 4 -49 5 1 2 2 11 11 4 The trick is to multiply all edge values by -1 then find the shortest path and then multiply the edge values by -1 again! Longest path on DAG What about the longest path? On a general graph this problem is NP-Hard, but on a DAG this problem is solvable in O(V+E)!
  • 549.
    A B C D E F G H -3 -6 -8 -4 4-9 -5 -1 -2 -2 -11 -11 -4 The trick is to multiply all edge values by -1 then find the shortest path and then multiply the edge values by -1 again! Longest path on DAG What about the longest path? On a general graph this problem is NP-Hard, but on a DAG this problem is solvable in O(V+E)!
  • 550.
    A B C D E F G H -3 -6 -8 -4 4-9 -5 -1 -2 -2 -11 -11 -4 The trick is to multiply all edge values by -1 then find the shortest path and then multiply the edge values by -1 again! (-3 + -11 + -9) * -1 = 23 Longest path on DAG What about the longest path? On a general graph this problem is NP-Hard, but on a DAG this problem is solvable in O(V+E)!
  • 551.
    Source Code Link Implementationsource code can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 553.
    Shortest & Longest pathon a DAG A B C D E F G H 3 6 8 4 -4 9 5 1 2 2 11 11 4
  • 554.
  • 555.
  • 556.
    What is Dijkstra’salgorithm? Dijkstra’s algorithm is a Single Source Shortest Path (SSSP) algorithm for graphs with non-negative edge weights. Depending on how the algorithm is implemented and what data structures are used the time complexity is typically O(E*log(V)) which is competitive against other shortest path algorithms.
  • 557.
    One constraint forDijkstra’s algorithm is that the graph must only contain non-negative edge weights. This constraint is imposed to ensure that once a node has been visited its optimal distance cannot be improved. Algorithm prerequisites This is property is especially important because it enables Dijkstra’s algorithm to act in a greedy manner by always selecting the next most promising node.
  • 558.
    • Lazy Dijkstra’sanimation • Lazy Dijkstra’s pseudo-code • Finding SP + stopping early optimization • Using indexed priority queue + decreaseKey to reduce space and increase performance. • Eager Dijkstra’s animation • Eager Dijkstra’s pseudo-code • Heap optimization with D-ary heap Outline The goal of this slide deck is for you to understand how to implement Dijkstra’s algorithm and implement it efficiently.
  • 559.
    Maintain a ‘dist’array where the distance to every node is positive infinity. Mark the distance to the start node ’s' to be 0. Maintain a PQ of key-value pairs of (node index, distance) pairs which tell you which node to visit next based on sorted min value. Insert (s, 0) into the PQ and loop while PQ is not empty pulling out the next most promising (node index, distance) pair. Iterate over all edges outwards from the current node and relax each edge appending a new (node index, distance) key-value pair to the PQ for every relaxation. Quick Algorithm Overview
  • 560.
  • 561.
    0 1 2 3 4 1 2 1 5 3 4 dist ∞∞ ∞ ∞ ∞ 0 1 2 3 4 Lazy Dijkstra’s
  • 562.
    0 1 2 3 4 1 2 1 5 3 4 dist ∞∞ ∞ ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs Lazy Dijkstra’s
  • 563.
    0 1 2 3 4 1 2 1 5 3 4 dist 0∞ ∞ ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) Lazy Dijkstra’s
  • 564.
    0 1 2 3 4 1 2 1 5 3 4 dist 0∞ ∞ ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) Lazy Dijkstra’s
  • 565.
    0 1 2 3 4 1 2 1 5 3 4 dist 04 ∞ ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) Best distance from node 0 to node 1 is: dist[0] + edge.cost = 0 + 4 = 4 Lazy Dijkstra’s
  • 566.
    0 1 2 3 4 1 2 1 5 3 4 dist 04 1 ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) Best distance from node 0 to node 2 is: dist[0] + edge.cost = 0 + 1 = 1 Lazy Dijkstra’s
  • 567.
    0 1 2 3 4 1 2 1 5 3 4 dist 04 1 ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) Lazy Dijkstra’s
  • 568.
    0 1 2 3 4 1 2 1 5 3 4 dist 04 1 ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) Lazy Dijkstra’s
  • 569.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 ∞ ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) Best distance from node 2 to node 1 is: dist[2] + edge.cost = 1 + 2 = 3 Lazy Dijkstra’s
  • 570.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 6 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) Best distance from node 2 to node 3 is: dist[2] + edge.cost = 1 + 5 = 6 Lazy Dijkstra’s
  • 571.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 6 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) Lazy Dijkstra’s
  • 572.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 6 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) Lazy Dijkstra’s
  • 573.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) Best distance from node 1 to node 3 is: dist[1] + edge.cost = 3 + 1 = 4 Lazy Dijkstra’s
  • 574.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) Lazy Dijkstra’s
  • 575.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) Lazy Dijkstra’s
  • 576.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) We have already found a better route to get to node 1 (since dist[1] has value 3) so we can ignore this entry in the PQ. Lazy Dijkstra’s
  • 577.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 ∞ 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) Lazy Dijkstra’s
  • 578.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) Best distance from node 3 to node 4 is: dist[3] + edge.cost = 4 + 3 = 7 Lazy Dijkstra’s
  • 579.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) Lazy Dijkstra’s
  • 580.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) Lazy Dijkstra’s
  • 581.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) We have already found a better route to get to node 3 (since dist[3] has value 4) so we can ignore this entry in the PQ. Lazy Dijkstra’s
  • 582.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) Lazy Dijkstra’s
  • 583.
    0 1 2 3 4 1 2 1 5 3 4 dist 03 1 4 7 0 1 2 3 4 (index, dist) key-value pairs (0, 0) (1, 4) (2, 1) (1, 3) (3, 6) (3, 4) (4, 7) Lazy Dijkstra’s
  • 584.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 585.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 586.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 587.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist Assume PQ stores (node index, best distance) pairs sorted by minimum distance.
  • 588.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 589.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 590.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 591.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 592.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist In practice most standard libraries do not support the decrease key operation for PQs. A way to get around this is to add a new (node index, best distance) pair every time we update the distance to a node.
  • 593.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist As a result, it is possible to have duplicate node indices in the PQ. This is not ideal, but inserting a new key-value pair in O(log(n)) is much faster than searching for the key in the PQ which takes O(n)
  • 594.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist
  • 595.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) return dist A neat optimization we can do which ignores stale (index, dist) pairs in our PQ is to skip nodes where we already found a better path routing through others nodes before we got to processing this node.
  • 596.
    Finding the optimalpath s e If you wish to not only find the optimal distance to a particular node but also what sequence of nodes were taken to get there you need to track some additional information.
  • 597.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s and # the prev array to reconstruct the shortest path itself # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n prev = [null, null, …, null] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: prev[edge.to] = index dist[edge.to] = newDist pq.insert((edge.to, newDist)) return (dist, prev)
  • 598.
    # Finds theshortest path between two nodes. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) # e - the index of the end node (0 ≤ e < n) function findShortestPath(g, n, s, e): dist, prev = dijkstra(g, n, s) path = [] if (dist[e] == ∞) return path for (at = e; at != null; at = prev[at]) path.add(at) path.reverse() return path
  • 599.
    Stopping Early Q: Supposeyou know the destination node you’re trying to reach is ‘e’ and you start at node ’s’ do you still have to visit every node in the graph? s e
  • 600.
    A: Yes, inthe worst case. However, it is possible to stop early once you have finished visiting the destination node. Stopping Early s e
  • 601.
    The main ideafor stopping early is that Dijkstra’s algorithm processes each next most promising node in order. So if the destination node has been visited, its shortest distance will not change as more future nodes are visited. Stopping Early s e
  • 602.
    # Runs Dijkstra’salgorithm and returns the shortest distance # between nodes ’s’ and ‘e’. If there is no path, ∞ is returned. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) # e - the index of the end node (0 ≤ e < n) function dijkstra(g, n, s, e): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 pq = empty priority queue pq.insert((s, 0)) while pq.size() != 0: index, minValue = pq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist pq.insert((edge.to, newDist)) if index == e: return dist[e] return ∞
  • 603.
    Eager Dijkstra’s usingan Indexed Priority Queue Our current lazy implementation of Dijkstra’s inserts duplicate key-value pairs (keys being the node index and the value being the shortest distance to get to that node) in our PQ because it’s more efficient to insert a new key-value pair in O(log(n)) than it is to update an existing key’s value in O(n). This approach is inefficient for dense graphs because we end up with several stale outdated key-value pairs in our PQ. The eager version of Dijkstra’s avoids duplicate key-value pairs and supports efficient value updates in O(log(n)) by using an Indexed Priority Queue (IPQ)
  • 604.
    Indexed Priority QueueDS Video <insert video clip> github.com/williamfiset/data-structures
  • 605.
    0 1 2 3 4 5 5 1 3 1 20 2 3 2 12 3 6 ∞ ∞ ∞∞ ∞ ∞ dist = (index, dist) key-value pairs 0 1 2 3 4 5 Eager Dijkstra’s
  • 606.
    0 1 2 3 4 5 5 1 3 1 20 2 3 2 12 3 6 dist = (index, dist) key-valuepairs 0 1 2 3 4 5 Eager Dijkstra’s (0, 0) 0 ∞ ∞ ∞ ∞ ∞
  • 607.
    0 1 2 3 4 5 5 1 1 2 3 2 3 12 20 3 6 0 ∞ ∞∞ ∞ ∞ dist = (index, dist) key-value pairs (0, 0) 0 1 2 3 4 5 Eager Dijkstra’s
  • 608.
    0 1 2 3 4 5 5 1 1 2 3 2 3 12 20 3 6 0 5 ∞∞ ∞ ∞ dist = (index, dist) key-value pairs (0, 0) (1, 5) 0 1 2 3 4 5 Best distance from node 0 to node 1: dist[0] + edge.cost = 0 + 5 = 5 Eager Dijkstra’s
  • 609.
    0 1 2 3 4 5 5 1 1 2 3 2 3 12 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 5) (2, 1) 0 5 1 ∞ ∞ ∞ 0 1 2 3 4 5 Best distance from node 0 to node 2: dist[0] + edge.cost = 0 + 1 = 1 Eager Dijkstra’s
  • 610.
    0 1 2 3 4 5 5 1 1 2 3 2 3 12 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 5) (2, 1) 0 5 1 ∞ ∞ ∞ 0 1 2 3 4 5 Eager Dijkstra’s
  • 611.
    0 1 2 3 4 5 5 1 1 2 3 2 3 12 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 5) (2, 1) 0 5 1 ∞ ∞ ∞ 0 1 2 3 4 5 Eager Dijkstra’s
  • 612.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 5) (2, 1) 0 5 1 ∞ 13 ∞ 0 1 2 3 4 5 (4, 13) Best distance from node 2 to node 4: dist[2] + edge.cost = 1 + 12 = 13 Eager Dijkstra’s
  • 613.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 ∞ 13 ∞ 0 1 2 3 4 5 (4, 13) Best distance from node 2 to node 1: dist[2] + edge.cost = 1 + 3 = 4 Eager Dijkstra’s
  • 614.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 ∞ 13 ∞ 0 1 2 3 4 5 (4, 13) Eager Dijkstra’s
  • 615.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 ∞ 13 ∞ 0 1 2 3 4 5 (4, 13) Eager Dijkstra’s
  • 616.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 ∞ 13 ∞ 0 1 2 3 4 5 (4, 13) Node 2 has already been visited so we cannot improve it’s already best distance Eager Dijkstra’s
  • 617.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 ∞ 13 ∞ (4, 13) 0 1 2 3 4 5 dist[1] + edge.cost = 4 + 20 = 24 > dist[4] = 13 so we cannot update best distance. Eager Dijkstra’s
  • 618.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 13 ∞ (4, 13) 0 1 2 3 4 5 (3, 7) Best distance from node 1 to node 3: dist[1] + edge.cost = 4 + 3 = 7 Eager Dijkstra’s
  • 619.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 13 ∞ (4, 13) 0 1 2 3 4 5 (3, 7) Eager Dijkstra’s
  • 620.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 13 ∞ (4, 13) 0 1 2 3 4 5 (3, 7) Eager Dijkstra’s
  • 621.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 13 ∞ (4, 13) 0 1 2 3 4 5 (3, 7) Node 2 is already visited, therefore we cannot improve on its best distance Eager Dijkstra’s
  • 622.
    0 1 2 3 4 5 5 1 12 1 2 3 2 3 20 3 6 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 ∞ (4, 9) 0 1 2 3 4 5 (3, 7) Best distance from node 3 to node 4: dist[3] + edge.cost = 7 + 2 = 9 Eager Dijkstra’s
  • 623.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 13 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 13) Best distance from node 3 to node 5: dist[3] + edge.cost = 7 + 6 = 13 Eager Dijkstra’s
  • 624.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 13 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 13) Eager Dijkstra’s
  • 625.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 13 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 13) Eager Dijkstra’s
  • 626.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 10 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 10) Best distance from node 4 to node 5: dist[3] + edge.cost = 9 + 1 = 10 Eager Dijkstra’s
  • 627.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 10 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 10) Eager Dijkstra’s
  • 628.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 10 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 10) Eager Dijkstra’s
  • 629.
    0 1 2 3 4 5 5 1 12 1 2 6 3 2 3 20 3 dist = (index, dist) key-valuepairs (0, 0) (1, 4) (2, 1) 0 4 1 7 9 10 (4, 9) 0 1 2 3 4 5 (3, 7) (5, 10) Eager Dijkstra’s
  • 630.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 ipq = empty indexed priority queue ipq.insert(s, 0) while ipq.size() != 0: index, minValue = ipq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist if !ipq.contains(edge.to): ipq.insert(edge.to, newDist) else: ipq.decreaseKey(edge.to, newDist) return dist
  • 631.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 ipq = empty indexed priority queue ipq.insert(s, 0) while ipq.size() != 0: index, minValue = ipq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist if !ipq.contains(edge.to): ipq.insert(edge.to, newDist) else: ipq.decreaseKey(edge.to, newDist) return dist
  • 632.
    # Runs Dijkstra’salgorithm and returns an array that contains # the shortest distance to every node from the start node s. # g - adjacency list of weighted graph # n - the number of nodes in the graph # s - the index of the starting node (0 ≤ s < n) function dijkstra(g, n, s): vis = [false, false, … , false] # size n dist = [∞, ∞, … ∞, ∞] # size n dist[s] = 0 ipq = empty indexed priority queue ipq.insert(s, 0) while ipq.size() != 0: index, minValue = ipq.poll() vis[index] = true if dist[index] < minValue: continue for (edge : g[index]): if vis[edge.to]: continue newDist = dist[index] + edge.cost if newDist < dist[edge.to]: dist[edge.to] = newDist if !ipq.contains(edge.to): ipq.insert(edge.to, newDist) else: ipq.decreaseKey(edge.to, newDist) return dist The main advantage to using decreaseKey is to prevent duplicate node indexes to be present in the PQ.
  • 633.
    D-ary Heap optimization Whenexecuting Dijkstra’s algorithm, especially on dense graphs, there are a lot more updates (i.e decreaseKey operations) to key-value pairs than there are dequeue (poll) operations. A D-ary heap is a heap variant in which each node has D children. This speeds up decrease key operations at the expense of more costly removals.
  • 634.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,5) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8)
  • 635.
    D-ary Heap (withD = 4) Suppose we want to update the node with index 6 to have a new shortest distance of 1 (a.k.a decreaseKey(6, 1)) (5,2) (3,3) (6,5) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8)
  • 636.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Suppose we want to update the node with index 6 to have a new shortest distance of 1 (a.k.a decreaseKey(6, 1)) Assuming we have an Indexed D-ary Heap we can update the key’s value in O(1) but we still need to adjust its position.
  • 637.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Suppose we want to update the node with index 6 to have a new shortest distance of 1 (a.k.a decreaseKey(6, 1))
  • 638.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Suppose we want to update the node with index 6 to have a new shortest distance of 1 (a.k.a decreaseKey(6, 1))
  • 639.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Suppose we want to update the node with index 6 to have a new shortest distance of 1 (a.k.a decreaseKey(6, 1)) The whole update took only two operations because the heap was very flat.
  • 640.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In contrast suppose we want to remove the root node.
  • 641.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In contrast suppose we want to remove the root node.
  • 642.
    D-ary Heap (withD = 4) (5,2) (3,3) (6,1) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In contrast suppose we want to remove the root node.
  • 643.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 644.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 645.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 646.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 647.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 648.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Value of 2 is the smallest In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 649.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 650.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 651.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 652.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 653.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 654.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Value of 3 is the smallest In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 655.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) In a D-ary heap we have to search D children of the current node to find the minimum (k, v) pair to swap downwards. In contrast suppose we want to remove the root node.
  • 656.
    D-ary Heap (withD = 4) (5,2) (3,3) … … … … … … … … … … … (1,7) (0,7) (1,9) (2,6) (8,5) (9,4) (4,8) Removals are clearly much more expensive, but they are also a lot less common in Dijkstra’s than decreaseKey operations.
  • 657.
    Optimal D-ary Heapdegree Q: What is the optimal D-ary heap degree to maximize performance of Dijkstra’s algorithm? A: In general D = E/V is the best degree to use to balance removals against decreaseKey operations improving Dijkstra’s time complexity to O(E*logE/V(V)) which is much better especially for dense graphs which have lots of decreaseKey operations.
  • 658.
    The state ofthe art The current state of the art as of now is the Fibonacci heap which gives Dijkstra’s algorithm a time complexity of O(E + Vlog(V)) However, in practice, Fibonacci heaps are very difficult to implement and have a large enough constant amortized overhead to make them impractical unless your graph is quite large.
  • 659.
    Source Code andSlides Implementation source code and slides can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 660.
  • 662.
  • 663.
  • 664.
  • 665.
  • 666.
    BF algorithm overview Ingraph theory, the Bellman-Ford (BF) algorithm is a Single Source Shortest Path (SSSP) algorithm. This means it can find the shortest path from one node to any other node. However, BF is not ideal for most SSSP problems because it has a time complexity of O(EV). It is better to use Dijkstra’s algorithm which is much faster. It is on the order of Θ((E+V)log(V)) when using a binary heap priority queue.
  • 667.
    BF algorithm overview However,Dijkstra’s algorithm can fail when the graph has negative edge weights. This is when BF becomes really handy because it can be used to detect negative cycles and determine where they occur. Finding negative cycles can be useful in many types of applications. One particularly neat application arises in finance when performing an arbitrage between two or more markets.
  • 668.
    Negative Cycles Negative cyclescan manifest themselves in many ways…
  • 669.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 1 2 3 4 5 4 -1 3 1 2 3 -2 6 2 2 Unaffected node Directly in negative cycle Reachable by negative cycle Starting node
  • 670.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 1 2 3 4 5 4 -1 3 1 2 3 -2 6 2 2 Starting node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 671.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 1 2 3 4 5 4 3 1 2 3 -2 6 2 2 -1 Starting node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 672.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 2 1 3 Starting node 4 5 3 1 1 1 4 -6 1 1 1 1 1 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 673.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 2 1 3 Starting node 4 5 3 1 1 1 4 -6 1 1 1 1 1 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 674.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 2 1 3 Starting node 4 5 3 1 1 1 4 -6 1 1 1 1 1 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 675.
    BF Algorithm Steps LetE be the number of edges. Let V be the number of vertices. Let S be the id of the starting node. Let D be an array of size V that tracks the best distance from S to each node. Let’s define a few variables…
  • 676.
    BF Algorithm Steps 1)Set every entry in D to +∞ 2) Set D[S] = 0 3) Relax each edge V-1 times:
  • 677.
    BF Algorithm Steps 1)Set every entry in D to +∞ 2) Set D[S] = 0 3) Relax each edge V-1 times: for (i = 0; i < V-1; i = i + 1): for edge in graph.edges: // Relax edge (update D with shorter path) if (D[edge.from] + edge.cost < D[edge.to]) D[edge.to] = D[edge.from] + edge.cost
  • 678.
    BF Algorithm Steps 1)Set every entry in D to +∞ 2) Set D[S] = 0 3) Relax each edge V-1 times: for (i = 0; i < V-1; i = i + 1): for edge in graph.edges: // Relax edge (update D with shorter path) if (D[edge.from] + edge.cost < D[edge.to]) D[edge.to] = D[edge.from] + edge.cost // Repeat to find nodes caught in a negative cycle for (i = 0; i < V-1; i = i + 1): for edge in graph.edges: if (D[edge.from] + edge.cost < D[edge.to]) D[edge.to] = -∞
  • 679.
    0 ∞ 1 ∞ 2∞ 3 ∞ 4 ∞ 5 ∞ 6 ∞ 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 680.
    0 0 1 ∞ 2∞ 3 ∞ 4 ∞ 5 ∞ 6 ∞ 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 681.
    0 0 1 5 2∞ 3 ∞ 4 ∞ 5 ∞ 6 ∞ 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 682.
    0 0 1 5 225 3 ∞ 4 ∞ 5 ∞ 6 ∞ 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 683.
    0 0 1 5 225 3 ∞ 4 ∞ 5 35 6 ∞ 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 684.
    0 0 1 5 225 3 ∞ 4 ∞ 5 35 6 65 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 685.
    0 0 1 5 225 3 35 4 ∞ 5 35 6 65 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 686.
    0 0 1 5 225 3 35 4 100 5 35 6 65 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 687.
    0 0 1 5 220 3 35 4 100 5 35 6 65 7 ∞ 8 ∞ 9 ∞ 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 688.
    0 0 1 5 220 3 35 4 100 5 35 6 65 7 ∞ 8 ∞ 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 689.
    0 0 1 5 220 3 35 4 60 5 35 6 65 7 ∞ 8 ∞ 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 690.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 ∞ 8 ∞ 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 691.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 ∞ 8 85 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 692.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 85 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 693.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 694.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Iteration 1 complete, 8 more to go… NOTE: The edges do not need to be chosen in any specific order.
  • 695.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 696.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 697.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 698.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 699.
    0 0 1 5 220 3 35 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 700.
    0 0 1 5 220 3 30 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 701.
    0 0 1 5 220 3 30 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 702.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 200 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 703.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 704.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 705.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 706.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 707.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 708.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node NOTE: The edges do not need to be chosen in any specific order.
  • 709.
    0 0 1 5 215 3 30 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Iteration 2 complete, 7 more to go… Let’s fast-forward to the end…
  • 710.
    0 0 1 5 2-20 3 -5 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node We’re finished with the SSSP part. Now let’s detect those negative cycles. If we can relax an edge then there’s a negative cycle.
  • 711.
    0 0 1 5 2-20 3 -5 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 712.
    0 0 1 5 2-20 3 -5 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 713.
    0 0 1 5 2-20 3 -5 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 714.
    0 0 1 5 2-20 3 -5 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 715.
    0 0 1 5 2-20 3 -∞ 4 60 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 716.
    0 0 1 5 2-20 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 717.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 160 0 1 6 2 5 7 4 8 9 3 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Unaffected node Directly in negative cycle Reachable by negative cycle
  • 718.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 9 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 719.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 9 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 720.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 9 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 721.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 9 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 722.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 9 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 723.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node 9 2 4 3 Unaffected node Directly in negative cycle Reachable by negative cycle
  • 724.
    0 0 1 5 2-∞ 3 -∞ 4 -∞ 5 35 6 40 7 -10 8 -20 9 -∞ 0 1 6 5 7 8 -15 10 5 -50 -10 50 60 20 25 100 30 5 75 Start node Repeat this for another 8 iterations in order to ensure the cycles fully propagate. In this example, we happened to detect all cycles on the first iteration, but this was a coincidence. 9 2 4 3
  • 726.
  • 727.
  • 728.
  • 729.
    FW algorithm overview Ingraph theory, the Floyd-Warshall (FW) algorithm is an All-Pairs Shortest Path (APSP) algorithm. This means it can find the shortest path between all pairs of nodes. The time complexity to run FW is O(V³) which is ideal for graphs no larger than a couple hundred nodes.
  • 730.
    Shortest Path (SP)Algorithms BFS Dijkstra’s Bellman Ford Floyd Warshall Complexity O(V+E) O((V+E)logV) O(VE) O(V³) Recommended graph size Large Large/ Medium Medium/ Small Small Good for APSP? Only works on unweighted graphs Ok Bad Yes Can detect negative cycles? No No Yes Yes SP on graph with weighted edges Incorrect SP answer Best algorithm Works Bad in general SP on graph with unweighted edges Best algorithm Ok Bad Bad in general Reference: Competitive Programming 3, P. 161, Steven & Felix Halim
  • 731.
    BFS Dijkstra’s Bellman Ford Floyd Warshall ComplexityO(V+E) O((V+E)logV) O(VE) O(V³) Recommended graph size Large Large/ Medium Medium/ Small Small Good for APSP? Only works on unweighted graphs Ok Bad Yes Can detect negative cycles? No No Yes Yes SP on graph with weighted edges Incorrect SP answer Best algorithm Works Bad in general SP on graph with unweighted edges Best algorithm Ok Bad Bad in general Shortest Path (SP) Algorithms Reference: Competitive Programming 3, P. 161, Steven & Felix Halim
  • 732.
    Graph setup With FW,the optimal way to represent our graph is with a 2D adjacency matrix m where cell m[i][j] represents the edge weight of going from node i to node j. 0 4 1 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D A C B D 4 9 1 3 6 11 4 1 2 6 5 -4
  • 733.
    Graph setup 0 41 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D A C B D 4 9 1 3 6 11 4 1 2 6 5 -4 NOTE: In the graph above, it is assumed that the distance from a node to itself is zero. This is why the diagonal is all zeros. With FW, the optimal way to represent our graph is with a 2D adjacency matrix m where cell m[i][j] represents the edge weight of going from node i to node j.
  • 734.
    Graph setup If thereis no edge from node i to node j then set the edge value for m[i][j] to be positive infinity. 0 4 1 ∞ ∞ 0 6 ∞ 4 1 0 2 ∞ ∞ ∞ 0 A B C D A B C D A C B D 4 1 6 4 1 2
  • 735.
    Graph setup 0 41 ∞ ∞ 0 6 ∞ 4 1 0 2 ∞ ∞ ∞ 0 A B C D A B C D A C B D 4 1 6 4 1 2 IMPORTANT: If your programming language does not support a special constant for +∞ such that ∞ + ∞ = ∞ and x + ∞ = ∞ then avoid using 231-1 as infinity! This will cause integer overflow; prefer to use a large constant such as 107 instead. If there is no edge from node i to node j then set the edge value for m[i][j] to be positive infinity.
  • 736.
    The main ideabehind the Floyd-Warshall algorithm is to gradually build up all intermediate routes between nodes i and j to find the optimal path.
  • 737.
    Suppose our adjacencymatrix tells us that the distance from a to b is: m[a][b] = 11 The main idea behind the Floyd-Warshall algorithm is to gradually build up all intermediate routes between nodes i and j to find the optimal path. a b 11
  • 738.
    The main ideabehind the Floyd-Warshall algorithm is to gradually build up all intermediate routes between nodes i and j to find the optimal path. a b 11 c 5 5 Suppose there exists a third node, c. If m[a][c] + m[c][b] < m[a][b] then it’s better to route through c!
  • 739.
    The goal ofFloyd-Warshall is to eventually consider going through all possible intermediate nodes on paths of different lengths.
  • 740.
    a b 11 c 5 ? 2 2 The goalof Floyd-Warshall is to eventually consider going through all possible intermediate nodes on paths of different lengths.
  • 741.
    a b 11 ? ? 32 c 1 1 a b 11 c 5 ? 2 2 The goal of Floyd-Warshall is to eventually consider going through all possible intermediate nodes on paths of different lengths.
  • 742.
    a b 11 c 1 ? 3 -2 ? 1 ? 0 a b 11 c 5 ? 2 2 Thegoal of Floyd-Warshall is to eventually consider going through all possible intermediate nodes on paths of different lengths. a b 11 ? ? 3 2 c 1 1
  • 743.
    dp[k][i][j] = shortestpath from i to j routing through nodes {0,1,…,k-1,k} Start with k = 0, then k = 1, then k = 2, … This gradually builds up the optimal solution routing through 0, then all optimal solutions routing through 0 and 1, then all optimal solutions routing through 0, 1, 2, etc… up until n-1 which stores to APSP solution. Let ‘dp’ (short for Dynamic Programming) be a 3D matrix of size n x n x n that acts as a memo table. The Memo Table Specifically dp[n-1] is the 2D matrix solution we’re after.
  • 744.
    In the beginningthe optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0
  • 745.
    In the beginningthe optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0 otherwise: dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
  • 746.
    Reuse the bestdistance from i to j with values routing through nodes {0,1,…,k-1} In the beginning the optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0 otherwise: dp[k][i][j] = min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])
  • 747.
    dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]) Find the best distance from i to j through node k reusing best solutions from {0,1,…,k-1} In the beginning the optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0 otherwise:
  • 748.
    dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]) The right side of the min function in english essentially says: “go from i to k” and then “go from k to j” In the beginning the optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0 otherwise:
  • 749.
    dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]) k i j Visually this looks like: dp[k-1][i][j] dp[k-1][i][k] dp[k-1][k][j] In the beginning the optimal solution from i to j is simply the distance in the adjacency matrix. dp[k][i][j] = m[i][j] if k = 0 otherwise:
  • 750.
    Currently we’re usingO(V³) memory since our memo table ‘dp’ has one dimension for each of k, i and j. Notice that we will be looping over k starting from 0, then 1, 2… and so fourth. The important thing to note here is that previous result builds off the last since we need state k-1 to compute state k. With that being said, it is possible to compute the solution for k in-place saving us a dimension of memory and reducing the space complexity to O(V²)!
  • 751.
    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]) otherwise: The new recurrence relation is: dp[i][j] = m[i][j] if k = 0 Currently we’re using O(V³) memory since our memo table ‘dp’ has one dimension for each of k, i and j. Notice that we will be looping over k starting from 0, then 1, 2… and so fourth. The important thing to note here is that previous result builds off the last since we need state k-1 to compute state k. With that being said, it is possible to compute the solution for k in-place saving us a dimension of memory and reducing the space complexity to O(V²)!
  • 752.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 753.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 754.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 755.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 756.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 757.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 758.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 759.
    function setup(m): dp =empty matrix of size n x n # Should contain null values by default next = empty integer matrix of size n x n # Do a deep copy of the input matrix and setup # the 'next' matrix for path reconstruction. for(i := 0; i < n; i++): for(j := 0; j < n; j++): dp[i][j] = m[i][j] if m[i][j] != +∞: next[i][j] = j
  • 760.
    function setup(m): dp =empty matrix of size n x n # Should contain null values by default next = empty integer matrix of size n x n # Do a deep copy of the input matrix and setup # the 'next' matrix for path reconstruction. for(i := 0; i < n; i++): for(j := 0; j < n; j++): dp[i][j] = m[i][j] if m[i][j] != +∞: next[i][j] = j
  • 761.
    function setup(m): dp =empty matrix of size n x n # Should contain null values by default next = empty integer matrix of size n x n # Do a deep copy of the input matrix and setup # the 'next' matrix for path reconstruction. for(i := 0; i < n; i++): for(j := 0; j < n; j++): dp[i][j] = m[i][j] if m[i][j] != +∞: next[i][j] = j
  • 762.
    function setup(m): dp =empty matrix of size n x n # Should contain null values by default next = empty integer matrix of size n x n # Do a deep copy of the input matrix and setup # the 'next' matrix for path reconstruction. for(i := 0; i < n; i++): for(j := 0; j < n; j++): dp[i][j] = m[i][j] if m[i][j] != +∞: next[i][j] = j
  • 763.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 764.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 765.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 766.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 767.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 768.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 769.
    Negative Cycles What dowe mean by a negative cycle?
  • 770.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 1 2 3 4 5 4 -1 3 1 2 3 -2 6 2 2 Unaffected node Directly in negative cycle
  • 771.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 1 2 3 4 5 4 -1 3 1 2 3 -2 6 2 2 Unaffected node Directly in negative cycle
  • 772.
    Negative Cycles Negative cyclescan manifest themselves in many ways… 0 2 3 4 5 4 3 1 2 3 -2 6 2 2 -1 1 Unaffected node Directly in negative cycle
  • 773.
    Negative Cycles Negative cyclescan manifest themselves in many ways… Unaffected node Directly in negative cycle 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 3
  • 774.
    Negative Cycles Negative cyclescan manifest themselves in many ways… Unaffected node Directly in negative cycle 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 3
  • 775.
    Negative Cycles 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 Unaffected node Directly in negativecycle 3 The important thing to ask ourselves is does the optimal path from node i to node j go through a red node? If so the path is affected by the negative cycle and is compromised.
  • 776.
    function propagateNegativeCycles(dp, n): #Execute FW APSP algorithm a second time but # this time if the distance can be improved # set the optimal distance to be -∞. # Every edge (i, j) marked with -∞ is either # part of or reaches into a negative cycle. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = -∞ next[i][j] = -1
  • 777.
    # Global/class scopevariables n = size of the adjacency matrix dp = the memo table that will contain APSP soln next = matrix used to reconstruct shortest paths function floydWarshall(m): setup(m) # Execute FW all pairs shortest path algorithm. for(k := 0; k < n; k++): for(i := 0; i < n; i++): for(j := 0; j < n; j++): if(dp[i][k] + dp[k][j] < dp[i][j]: dp[i][j] = dp[i][k] + dp[k][j] next[i][j] = next[i][k] # Detect and propagate negative cycles. propagateNegativeCycles(dp, n) # Return APSP matrix return dp
  • 778.
    # Reconstructs theshortest path between nodes # ’start’ and ‘end’. You must run the # floydWarshall solver before calling this method. # Returns null if path if affected by negative cycle. function reconstructPath(start, end): path = [] # Check if there exists a path between # the start and the end node. if dp[start][end] == +∞: return path at := start # Reconstruct path from next matrix for(;at != end; at = next[at][end]): if at == -1: return null path.add(at) if next[at][end] == -1: return null path.add(end) return path
  • 779.
    Source Code Link Implementationsource code can be found at the following link: github.com/williamfiset/algorithms Link in the description
  • 780.
  • 782.
    Floyd-Warshall All Pairs ShortestPath Unaffected node Directly in negative cycle 0 2 1 3 4 5 1 1 1 4 -6 1 1 1 1 6 3
  • 783.
  • 784.
    Algorithm to Find Bridgesand Articulation Points William Fiset
  • 785.
    What are bridges& articulation points? A bridge / cut edge is any edge in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7
  • 786.
    A bridge /cut edge is any edge in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7 What are bridges & articulation points?
  • 787.
    An articulation point/ cut vertex is any node in a graph whose removal increases the number of connected components. 0 1 2 5 3 6 4 8 7 What are bridges & articulation points?
  • 788.
    0 1 2 5 3 6 4 8 7 An articulation point/ cut vertex is any node in a graph whose removal increases the number of connected components. What are bridges & articulation points?
  • 789.
    Bridges and articulationpoints are important in graph theory because they often hint at weak points, bottlenecks or vulnerabilities in a graph. Therefore, it’s important to be able to quickly find/detect when and where these occur. Both problems are related so we will develop an algorithm to find bridges and then modify it slightly to find articulation points. What are bridges & articulation points?
  • 790.
    Bridges algorithm Start atany node and do a Depth First Search (DFS) traversal labeling nodes with an increasing id value as you go. Keep track the id of each node and the smallest low- link value. During the DFS, bridges will be found where the id of the node your edge is coming from is less than the low link value of the node your edge is going to. NOTE: The low-link value of a node is defined as the smallest [lowest] id reachable from that node when doing a DFS (including itself).
  • 791.
    Undirected edge Directededge DFS traversal
  • 792.
  • 793.
  • 794.
  • 795.
  • 796.
  • 797.
  • 798.
  • 799.
  • 800.
  • 801.
  • 802.
    0 1 2 5 3 6 4 8 7 The low-link valueof a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. Undirected edge Directed edge
  • 803.
    0 1 2 3 4 5 6 7 8 Initially alllow-link values can be initialized to the node ids. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 804.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 805.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 806.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 807.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 808.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 809.
    0 1 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 810.
    0 0 2 3 4 5 6 7 8 The low-linkvalue of node 1 is 0 since node 0 is reachable from node 1. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 811.
    0 0 2 3 4 5 6 7 8 The low-linkvalue of node 2 is 0 since node 0 is reachable from node 2. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 812.
    0 0 2 3 4 5 6 7 8 The low-linkvalue of node 2 is 0 since node 0 is reachable from node 2. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 813.
    0 0 2 3 4 5 6 7 8 The low-linkvalue of node 2 is 0 since node 0 is reachable from node 2. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 814.
    0 0 2 3 4 5 6 7 8 The low-linkvalue of node 2 is 0 since node 0 is reachable from node 2. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 Undirected edge Directed edge
  • 815.
    0 0 0 3 4 5 6 7 8 The low-linkvalue of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 The low-link value of node 2 is 0 since node 0 is reachable from node 2. Undirected edge Directed edge
  • 816.
    0 0 3 4 5 6 7 8 Cannot updatelow-link values for nodes 3, 4 and 5. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 817.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 818.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 819.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 820.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 821.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 822.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 823.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 824.
    0 0 3 4 5 6 7 8 Node 6’slow-link value can be updated to 5 since node 5 is reachable from node 6. The low-link value of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 1 2 5 3 6 4 8 7 0 Undirected edge Directed edge
  • 825.
    0 0 3 4 5 5 7 8 The low-linkvalue of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 0 1 2 5 3 6 4 8 7 Node 6’s low-link value can be updated to 5 since node 5 is reachable from node 6. Undirected edge Directed edge
  • 826.
    0 0 3 4 5 5 5 5 The low-linkvalue of a node is defined as the smallest [lowest] id reachable from that node using forward and backward edges. 0 0 1 2 5 3 6 4 8 7 Node 6’s low-link value can be updated to 5 since node 5 is reachable from node 6. Undirected edge Directed edge
  • 827.
    0 0 3 4 5 5 5 5 Now noticethat the condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to)* 0 0 1 2 5 3 6 4 8 7 Is bridge since 2 < 3 Is bridge since 3 < 4 Is bridge since 2 < 5 * Where e.from is the node the directed edge starts at and e.to is the node the directed edge ends at. Undirected edge Directed edge
  • 828.
    0 0 3 4 2 2 2 2 0 0 1 2 5 3 6 4 8 7 Is bridge since2 < 3 Is bridge since 3 < 4 * Where e.from is the node the directed edge starts at and e.to is the node the directed edge ends at. Now notice that the condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to)* Undirected edge Directed edge
  • 829.
    Complexity What’s the runtimeof our algorithm to find bridges? Right now we’re doing one DFS to label all the nodes plus V more DFSs to find all the low-link values, giving us roughly: O(V(V+E)) Fortunately, we are able do better by updating the low-link values in one pass for O(V+E)
  • 830.
    id = 0 g= adjacency list with undirected edges n = size of the graph # In these arrays index i represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n visited = [false, …, false] # Length n function findBridges(): bridges = [] # Finds all bridges in the graph across # various connected components. for (i = 0; i < n; i = i + 1): if (!visited[i]): dfs(i, -1, bridges) return bridges
  • 831.
    id = 0 g= adjacency list with undirected edges n = size of the graph # In these arrays index i represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n visited = [false, …, false] # Length n function findBridges(): bridges = [] # Finds all bridges in the graph across # various connected components. for (i = 0; i < n; i = i + 1): if (!visited[i]): dfs(i, -1, bridges) return bridges
  • 832.
    # Perform DepthFirst Search (DFS) to find bridges. # at = current node, parent = previous node. The # bridges list is always of even length and indexes # (2*i, 2*i+1) form a bridge. For example, nodes at # indexes (0, 1) are a bridge, (2, 3) is another etc... function dfs(at, parent, bridges): visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(to, at, bridges) low[at] = min(low[at], low[to]) if (ids[at] < low[to]): bridges.add(at) bridges.add(to) else: low[at] = min(low[at], ids[to])
  • 833.
    # Perform DepthFirst Search (DFS) to find bridges. # at = current node, parent = previous node. The # bridges list is always of even length and indexes # (2*i, 2*i+1) form a bridge. For example, nodes at # indexes (0, 1) are a bridge, (2, 3) is another etc... function dfs(at, parent, bridges): visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(to, at, bridges) low[at] = min(low[at], low[to]) if (ids[at] < low[to]): bridges.add(at) bridges.add(to) else: low[at] = min(low[at], ids[to])
  • 834.
    # Perform DepthFirst Search (DFS) to find bridges. # at = current node, parent = previous node. The # bridges list is always of even length and indexes # (2*i, 2*i+1) form a bridge. For example, nodes at # indexes (0, 1) are a bridge, (2, 3) is another etc... function dfs(at, parent, bridges): visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(to, at, bridges) low[at] = min(low[at], low[to]) if (ids[at] < low[to]): bridges.add(at) bridges.add(to) else: low[at] = min(low[at], ids[to])
  • 835.
  • 836.
  • 837.
  • 838.
  • 839.
  • 840.
  • 841.
  • 842.
  • 843.
    # Perform DepthFirst Search (DFS) to find bridges. # at = current node, parent = previous node. The # bridges list is always of even length and indexes # (2*i, 2*i+1) form a bridge. For example, nodes at # indexes (0, 1) are a bridge, (2, 3) is another etc... function dfs(at, parent, bridges): visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(to, at, bridges) low[at] = min(low[at], low[to]) if (ids[at] < low[to]): bridges.add(at) bridges.add(to) else: low[at] = min(low[at], ids[to])
  • 844.
  • 845.
  • 846.
  • 847.
    0 1 2 3 4 0 1 0 3 4 VisitedUnvisited Current Undirected edge Directed edge
  • 848.
    0 1 2 3 4 0 1 0 3 4 VisitedUnvisited Current Undirected edge Directed edge
  • 849.
    0 1 2 3 4 0 1 0 3 4 VisitedUnvisited Current Undirected edge Directed edge
  • 850.
    0 1 2 3 4 0 1 0 3 4 VisitedUnvisited Current Undirected edge Directed edge
  • 851.
    0 1 2 5 3 4 0 1 0 3 4 5 VisitedUnvisited Current Undirected edge Directed edge
  • 852.
    0 1 2 5 3 4 0 1 0 3 4 5 VisitedUnvisited Current Undirected edge Directed edge
  • 853.
  • 854.
  • 855.
  • 856.
  • 857.
  • 858.
  • 859.
  • 860.
  • 861.
    # Perform DepthFirst Search (DFS) to find bridges. # at = current node, parent = previous node. The # bridges list is always of even length and indexes # (2*i, 2*i+1) form a bridge. For example, nodes at # indexes (0, 1) are a bridge, (2, 3) is another etc... function dfs(at, parent, bridges): visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(to, at, bridges) low[at] = min(low[at], low[to]) if (ids[at] < low[to]): bridges.add(at) bridges.add(to) else: low[at] = min(low[at], ids[to])
  • 862.
  • 863.
  • 864.
  • 865.
  • 866.
  • 867.
  • 868.
    0 1 2 5 3 6 4 8 7 0 0 3 4 5 5 5 5 0 Is bridge since2 < 5 The condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to) Undirected edge Directed edge
  • 869.
    0 1 2 5 3 6 4 8 7 0 0 3 4 5 5 5 5 0 Is bridge since2 < 3 The condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to) Undirected edge Directed edge
  • 870.
    0 1 2 5 3 6 4 8 7 0 0 3 4 5 5 5 5 0 Is bridge since3 < 4 The condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to) Undirected edge Directed edge
  • 871.
    0 1 2 5 3 6 4 8 7 0 0 3 4 5 5 5 5 0 Is bridge since3 < 4 The condition for a directed edge ‘e’ to have nodes that belong to a bridge is when the id(e.from) < lowlink(e.to) Is bridge since 2 < 3 Is bridge since 2 < 5 Undirected edge Directed edge
  • 872.
    Articulation points Articulation pointsare related very closely to bridges. It won’t take much modification to the finding bridges algorithm to find articulation points.
  • 873.
    Articulation points Simple observationabout articulation points: On a connected component with three or more vertices if an edge (u, v) is a bridge then either u or v is an articulation point.
  • 874.
    Articulation points Simple observationabout articulation points: On a connected component with three or more vertices if an edge (u, v) is a bridge then either u or v is an articulation point. 0 1 2 3 Bridge Articulation point
  • 875.
    Articulation points However, thiscondition alone is not sufficient to capture all articulation points. There exist cases where there is an articulation point without a bridge: 0 1 2 3 4 There are no bridges but node 2 is an articulation point since its removal would cause the graph to split into two components.
  • 876.
  • 877.
  • 878.
  • 879.
  • 880.
  • 881.
  • 882.
  • 883.
  • 884.
  • 885.
  • 886.
  • 887.
  • 888.
    Articulation points 0 1 3 4 2 5 0 0 0 0 0 0 Onthe callback, if id(e.from) == lowlink(e.to) then there was a cycle. The indication of a cycle back to the original node implies an articulation point.
  • 889.
    Articulation points The onlytime id(e.from) == lowlink(e.to) fails is when the starting node has 0 or 1 outgoing directed edges. This is because either the node is a singleton (0 case) or the node in trapped in a cycle (1 case). 1 0 3 2 0 0 0 0 Start node Here the condition is met, but the starting node only has 1 outgoing edge. Therefore, the start node is not an articulation point.
  • 890.
    Articulation points The onlytime id(e.from) == lowlink(e.to) fails is when the starting node has 0 or 1 outgoing directed edges. This is because either the node is a singleton (0 case) or the node in trapped in a cycle (1 case). 1 0 3 2 0 0 0 0 Start node However, when there are more than 1 outgoing edges the starting node can escape the cycle and thus becomes an articulation point! 4 4
  • 891.
    id = 0 g= adjacency list with undirected edges n = size of the graph outEdgeCount = 0 # In these arrays index i represents node i low = [0, 0, … 0, 0] # Length n ids = [0, 0, … 0, 0] # Length n visited = [false, …, false] # Length n isArt = [false, …, false] # Length n function findArtPoints(): for (i = 0; i < n; i = i + 1): if (!visited[i]): outEdgeCount = 0 # Reset edge count dfs(i, i, -1) isArt[i] = (outEdgeCount > 1) return isArt
  • 892.
    # Perform DFSto find articulation points. function dfs(root, at, parent): if (parent == root): outEdgeCount++ visited[at] = true id = id + 1 low[at] = ids[at] = id # For each edge from node ‘at’ to node ‘to’ for (to : g[at]): if to == parent: continue if (!visited[to]): dfs(root, to, at) low[at] = min(low[at], low[to]) # Articulation point found via bridge if (ids[at] < low[to]): isArt[at] = true # Articulation point found via cycle if (ids[at] == low[to]): isArt[at] = true else: low[at] = min(low[at], ids[to]) Being explicit here. However, this could just be a <= clause.
  • 893.
    Source Code Link Slides/sourcecode can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 895.
  • 896.
  • 897.
  • 898.
    Tarjan’s Algorithm for FindingStrongly Connected Components William Fiset
  • 899.
    What are SCCs? StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle.
  • 900.
    What are SCCs? StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle.
  • 901.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself).
  • 902.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 0
  • 903.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0
  • 904.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 2
  • 905.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 2 3
  • 906.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3
  • 907.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 908.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 909.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 910.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 911.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 912.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 913.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 914.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 915.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 916.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 917.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 918.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 919.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 920.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 921.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 922.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 923.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 3 5
  • 924.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 925.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 926.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 927.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 928.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 929.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 930.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 931.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 932.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 933.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 934.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5
  • 935.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5 0 The low-link of node 1 is 0, since node 0 is node with the lowest id reachable from node 1
  • 936.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). The low-link of node 3 is 2, since node 2 is node with the lowest id reachable from node 3 1 0 4 2 6 3 5 0 0 0 2 2 4 4
  • 937.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5 0 0 0 2 2 4 4
  • 938.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 1 0 4 2 6 3 5 0 0 0 2 2 4 4
  • 939.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself).
  • 940.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 4 6 1 3 5 2 0 Started DFS on the rightmost node, resumed DFS on node 2, and then resumed DFS on node 4 to finish labelling all nodes in the graph.
  • 941.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). 4 6 1 3 5 2 0 0
  • 942.
    Low-Link Values The low-linkvalue of a node is the smallest [lowest] node id reachable from that node when doing a DFS (including itself). All low link values are the same, but there are multiple SCCs! 4 6 1 3 5 2 0 IMPORTANT: Depending on where the DFS starts, and the order in which nodes/edges are visited, the low-link values for identifying SCCs could be wrong. In the context of Tarjan’s SCC algorithm, we maintain an invariant that prevents SCCs to interfere with the low link value of other SCCs. 0 0 0 0 0 0 0
  • 943.
    The Stack Invariant Tocope with the random traversal order of the DFS, Tarjan’s algorithm maintains a set (often as a stack) of valid nodes from which to update low-link values from. Nodes are added to the stack [set] of valid nodes as they’re explored for the first time. Nodes are removed from the stack [set] each time a complete SCC is found.
  • 944.
    If u andv are nodes in a graph and we’re currently exploring u then our new low- link update condition is that: New low-link update condition To update node u’s low-link value to node v’s low-link value there has to be a path of edges from u to v and node v must be on the stack.
  • 945.
    Another difference we’regoing to make to finding all low-link values is that instead of finding low-link values after the fact we’re going to update them “on the fly” during the DFS so we can get a linear O(V+E) time complexity :) Time Complexity
  • 946.
    Tarjan’s Algorithm Overview Markthe id of each node as unvisited. Start DFS. Upon visiting a node assign it an id and a low-link value. Also mark current nodes as visited and add them to a seen stack. On DFS callback, if the previous node is on the stack then min the current node’s low-link value with the last node’s low-link value*. After visiting all neighbors, if the current node started a connected component** then pop nodes off stack until current node is reached. **As we will see, a node started a connected component if its id equals its low link value *This allows low-link values to propagate throughout cycles.
  • 947.
    Stack Unvisited Visiting neighbours Visited all neighbours If anode’s colour is grey or orange then it is on the stack and we can update its low-link value.
  • 948.
  • 949.
  • 950.
  • 951.
  • 952.
  • 953.
  • 954.
    0 2 1 0 Stack 1 2 0 1 0 lowlink[2] =min(lowlink[2], lowlink[0]) = 0 Unvisited Visiting neighbours Visited all neighbours
  • 955.
  • 956.
    0 2 1 0 Stack 1 2 0 0 0 lowlink[1] =min(lowlink[1], lowlink[2]) = 0 Unvisited Visiting neighbours Visited all neighbours
  • 957.
  • 958.
    0 2 1 0 Stack 1 2 0 0 0 lowlink[0] =min(lowlink[0], lowlink[1]) = 0 Unvisited Visiting neighbours Visited all neighbours
  • 959.
    0 2 1 0 Stack 1 2 0 0 0 When acompleted SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 960.
    0 2 1 0 Stack 1 0 0 0 When acompleted SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 961.
    0 2 1 0 Stack 0 0 0 When acompleted SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 962.
    0 2 1 Stack 0 0 0 When acompleted SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 963.
    0 2 1 Stack 0 0 0 We’re notdone exploring the graph so pick another starting node at random. Unvisited Visiting neighbours Visited all neighbours
  • 964.
  • 965.
  • 966.
  • 967.
  • 968.
  • 969.
  • 970.
    4 3 5 0 2 1 Stack 0 0 0 3 3 4 4 5 5 Unvisited Visiting neighbours Visitedall neighbours Node 0 is not on stack so don’t min with its low-link value.
  • 971.
  • 972.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 6 6 Unvisited Visiting neighbours Visited all neighbours
  • 973.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 6 6 Unvisited Visiting neighbours Visited all neighbours
  • 974.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 6 6 Node 2 is not on stack so don’t min with its low-link value. Unvisited Visiting neighbours Visited all neighbours
  • 975.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 6 6 Unvisited Visiting neighbours Visited all neighbours
  • 976.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 4 6 lowlink[6] = min(lowlink[6], lowlink[4]) = 4 Unvisited Visiting neighbours Visited all neighbours
  • 977.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 4 6 Unvisited Visiting neighbours Visited all neighbours
  • 978.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 4 6 Node 0 is not on stack so don’t min with its low-link value. Unvisited Visiting neighbours Visited all neighbours
  • 979.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 5 4 6 Unvisited Visiting neighbours Visited all neighbours
  • 980.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 4 4 6 lowlink[5] = min(lowlink[5], lowlink[6]) = 4 Unvisited Visiting neighbours Visited all neighbours
  • 981.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 4 4 6 Unvisited Visiting neighbours Visited all neighbours
  • 982.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 4 4 6 lowlink[4] = min(lowlink[4], lowlink[5]) = 4 Unvisited Visiting neighbours Visited all neighbours
  • 983.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 4 4 6 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 984.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 5 4 4 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 985.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 4 4 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 986.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 4 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. Unvisited Visiting neighbours Visited all neighbours
  • 987.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 4 Unvisited Visiting neighbours Visited all neighbours
  • 988.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 4 Node 4 is not on stack so don’t min with its low-link value. Unvisited Visiting neighbours Visited all neighbours
  • 989.
    4 3 6 5 0 2 1 Stack 00 0 3 3 4 4 4 Unvisited Visiting neighbours Visited all neighbours
  • 990.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 7 7 Unvisited Visiting neighbours Visited all neighbours
  • 991.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 Unvisited Visiting neighbours Visited all neighbours 7 7
  • 992.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 Node 5 is not on stack so don’t min with its low-link value. Unvisited Visiting neighbours Visited all neighbours 7 7
  • 993.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 Unvisited Visiting neighbours Visited all neighbours 7 7
  • 994.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 3 7 lowlink[6] = min(lowlink[6], lowlink[3]) = 3 Unvisited Visiting neighbours Visited all neighbours
  • 995.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 3 7 Unvisited Visiting neighbours Visited all neighbours
  • 996.
    4 3 6 5 7 0 2 1 Stack 00 0 3 3 4 4 4 3 lowlink[3] = min(lowlink[3], lowlink[6]) = 3 Unvisited Visiting neighbours Visited all neighbours 7
  • 997.
    4 3 6 5 7 0 2 1 Stack 00 0 3 4 4 4 7 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. 3 3 Unvisited Visiting neighbours Visited all neighbours
  • 998.
    4 3 6 5 7 0 2 1 Stack 00 0 3 4 4 4 When a completed SCC is found (current node has visited all its neighbours and its lowlink value equals its id) pop off all associated nodes off the stack. 3 3 Unvisited Visiting neighbours Visited all neighbours
  • 999.
    4 3 6 5 7 0 2 1 00 0 3 4 4 4 3 Unvisited Visiting neighbours Visited all neighbours
  • 1000.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1001.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1002.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1003.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1004.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1005.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1006.
    UNVISITED = -1 n= number of nodes in graph g = adjacency list with directed edges id = 0 # Used to give each node an id sccCount = 0 # Used to count number of SCCs found # Index i in these arrays represents node i ids = [0, 0, … 0, 0] # Length n low = [0, 0, … 0, 0] # Length n onStack = [false, false, …, false] # Length n stack = an empty stack data structure function findSccs(): for(i = 0; i < n; i++): ids[i] = UNVISITED for(i = 0; i < n; i++): if(ids[i] == UNVISITED): dfs(i) return low
  • 1007.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1008.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1009.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1010.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1011.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1012.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1013.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1014.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1015.
    function dfs(at): stack.push(at) onStack[at] =true ids[at] = low[at] = id++ # Visit all neighbours & min low-link on callback for(to : g[at]): if(ids[to] == UNVISITED): dfs(to) if(onStack[to]): low[at] = min(low[at],low[to]) # After having visited all the neighbours of ‘at’ # if we're at the start of a SCC empty the seen # stack until we’re back to the start of the SCC. if(ids[at] == low[at]): for(node = stack.pop();;node = stack.pop()): onStack[node] = false low[node] = ids[at] if(node == at): break sccCount++
  • 1017.
  • 1018.
  • 1019.
  • 1020.
  • 1021.
    What are SCCs? StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle.
  • 1022.
    What are SCCs? StronglyConnected Components (SCCs) can be thought of as self-contained cycles within a directed graph where every vertex in a given cycle can reach every other vertex in the same cycle.
  • 1023.
    Kosaraju’s algorithm intuition 01 3 2 6 4 5 7 We are able to find a graph traversal ordering which enables us to visit SCCs
  • 1024.
    Kosaraju’s algorithm intuition 01 6 2 3 7 8 4 5 9 0 1 6 2 3 7 8 4 5 9
  • 1025.
    Kosaraju’s algorithm intuition 01 6 2 3 7 8 4 5 9 0 1 6 2 3 7 8 4 5 9 [0, 1, 6, 2, 3, 7, 4, 5, 9, 8]
  • 1026.
    Kosaraju’s algorithm intuition 10 2 4 5 3 [1, 3, 0, 5, 2, 4] 1 0 2 4 5 3 Nodes than can reach
  • 1027.
    Kosaraju’s algorithm intuition 02 1 7 4 3 6 5 [0, 3, 2, 1, 7, 6, 5, 4] 0 2 1 7 4 3 6 5 The reverse post order ordering presents the nodes in a order in which the node(s) which leak the most appear first. In G^T, these nodes will be those which are part the "smallest SCC", that is the SCC which leak the least
  • 1028.
  • 1029.
  • 1030.
    Traveling Salesman Problem (TSP)with Dynamic Programming William Fiset
  • 1031.
    What is theTSP? "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city exactly once and returns to the origin city?” - Wiki
  • 1032.
    What is theTSP? In other words, the problem is: given a complete graph with weighted edges (as an adjacency matrix) what is the Hamiltonian cycle (path that visits every node once) of minimum cost? 0 4 1 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D A C B D 4 9 1 3 6 11 4 1 2 6 5 -4
  • 1033.
    What is theTSP? 0 4 1 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D A C B D 9 3 1 -4 Tour cost: 9 + -4 + 1 + 3 = 9 Full tour: A -> D -> C -> B -> A In other words, the problem is: given a complete graph with weighted edges (as an adjacency matrix) what is the Hamiltonian cycle (path that visits every node once) of minimum cost?
  • 1034.
    What is theTSP? Finding the optimal solution to the TSP problem is very hard; in fact, the problem is known to be NP-Complete.
  • 1035.
    Brute force solution Thebrute force way to solve the TSP is to compute the cost of every possible tour. This means we have to try all possible permutations of node orderings which takes O(n!) time. 0 4 1 9 3 0 6 11 4 1 0 2 6 5 -4 0 A B C D A B C D Tour Cost A B C D 18 C A B D 15 A B D C 15 C A D B 24 A C B D 19 C B A D 9 A C D B 11 C B D A 19 A D B C 24 C D A B 18 A D C B 9 C D B A 11 B A C D 11 D A B C 18 B A D C 9 D A C B 19 B C A D 24 D B A C 11 B C D A 18 D B C A 24 B D A C 19 D C A B 15 B D C A 15 D C B A 9
  • 1036.
    TSP with DP Thedynamic programming solution to the TSP problem significantly improves on the time complexity, taking it from O(n!) to O(n22n). At first glance, this may not seem like a substantial improvement, however, it now makes solving this problem feasible on graphs with up to roughly 23 nodes on a typical computer.
  • 1037.
    TSP with DP nn! n22n 1 1 2 2 2 16 3 6 72 4 24 256 5 120 800 6 720 2304 … … … 15 1307674368000 7372800 16 20922789888000 16777216 17 355687428096000 37879808
  • 1038.
    TSP with DP Themain idea will be to compute the optimal solution for all the subpaths of length N while using information from the already known optimal partial tours of length N-1.
  • 1039.
    TSP with DP Beforestarting, make sure to select a node 0 ≤ S < N to be the designated starting node for the tour.
  • 1040.
    TSP with DP 0 2 1 3 Startof tour Before starting, make sure to select a node 0 ≤ S < N to be the designated starting node for the tour. For this example let S = node 0
  • 1041.
    TSP with DP 0 2 1 3 Next,compute and store the optimal value from S to each node X (≠ S). This will solve TSP problem for all paths of length n = 2.
  • 1042.
    TSP with DP 0 2 1 3 0 2 1 3 0 2 1 3 Next,compute and store the optimal value from S to each node X (≠ S). This will solve TSP problem for all paths of length n = 2.
  • 1043.
    TSP with DP Tocompute the optimal solution for paths of length 3, we need to remember (store) two things from each of the n = 2 cases: 1) The set of visited nodes in the subpath 2) The index of the last visited node in the path Together these two things form our dynamic programming state. There are N possible nodes that we could have visited last and 2N possible subsets of visited nodes. Therefore the space needed to store the answer to each subproblem is bounded by O(N2N).
  • 1044.
    The best wayto represent the set of visited nodes is to use a single 32-bit integer. A 32-bit int is compact, quick and allows for easy caching in a memo table. Visited Nodes as a Bit Field
  • 1045.
    Visited Nodes asa Bit Field The best way to represent the set of visited nodes is to use a single 32-bit integer. A 32-bit int is compact, quick and allows for easy caching in a memo table. 0 2 1 3 0 2 1 3 0 2 1 3 State Binary rep: 00112 = 3 Last node: 1 State Binary rep: 10012 = 9 Last node: 3 State Binary rep: 01012 = 5 Last node: 2
  • 1046.
    TSP with DP 0 2 1 3 State Binaryrep: 10012 = 9 Last node: 3 To solve 3 ≤ n ≤ N, we’re going to take the solved subpaths from n-1 and add another edge extending to a node which has not already been visited from the last visited node (which has been saved).
  • 1047.
    TSP with DP 0 2 1 3 State Binaryrep: 10012 = 9 Last node: 3 0 2 1 3 State Binary rep: 10112 = 11 Last node: 1 0 2 1 3 State Binary rep: 11012 = 13 Last node: 2 To solve 3 ≤ n ≤ N, we’re going to take the solved subpaths from n-1 and add another edge extending to a node which has not already been visited from the last visited node (which has been saved).
  • 1048.
    To complete theTSP tour, we need to connect our tour back to the start node S. TSP with DP Loop over the end state* in the memo table for every possible end position and minimize the lookup value plus the cost of going back to S. * The end state is the state where the binary representation is composed of N 1’s
  • 1049.
    In the nextfew slides we’ll look at some pseudocode for the TSP problem. TSP Pseudocode WARNING: The following slides make use of advanced bit manipulation techniques. Make sure you’re very comfortable with the binary operators <<, &, | and ^
  • 1050.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1051.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1052.
    # Initializes thememo table by caching # the optimal solution from the start # node to every other node. function setup(m, memo, S, N): for (i = 0; i < N; i = i + 1): if i == S: continue # Store the optimal value from node S # to each node i (this is given as input # in the adjacency matrix m). memo[i][1 << S | 1 << i] = m[S][i]
  • 1053.
    0 2 1 3 0 2 1 3 0 2 1 3 State Binary rep: 00112= 3 Last node: 1 State Binary rep: 10012 = 9 Last node: 3 State Binary rep: 01012 = 5 Last node: 2 Node S Node i
  • 1054.
    # Initializes thememo table by caching # the optimal solution from the start # node to every other node. function setup(m, memo, S, N): for (i = 0; i < N; i = i + 1): if i == S: continue # Store the optimal value from node S # to each node i (this is given as input # in the adjacency matrix m). memo[i][1 << S | 1 << i] = m[S][i]
  • 1055.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1056.
    function solve(m, memo,S, N): for (r = 3; r <= N; r++): # The combinations function generates all bit sets # of size N with r bits set to 1. For example, # combinations(3, 4) = {01112, 10112, 11012, 11102} for subset in combinations(r, N): if notIn(S, subset): continue for (next = 0; next < N; next = next + 1): if next == S || notIn(next, subset): continue # The subset state without the next node state = subset ^ (1 << next) minDist = +∞ # ‘e’ is short for end node. for (e = 0; e < N; e = e + 1): if e == S || e == next || notIn(e, subset)): continue newDistance = memo[e][state] + m[e][next] if (newDistance < minDist): minDist = newDistance memo[next][subset] = minDist # Returns true if the ith bit in ‘subset’ is not set function notIn(i, subset): return ((1 << i) & subset) == 0
  • 1057.
    function solve(m, memo,S, N): for (r = 3; r <= N; r++): # The combinations function generates all bit sets # of size N with r bits set to 1. For example, # combinations(3, 4) = {01112, 10112, 11012, 11102} for subset in combinations(r, N): if notIn(S, subset): continue for (next = 0; next < N; next = next + 1): if next == S || notIn(next, subset): continue # The subset state without the next node state = subset ^ (1 << next) minDist = +∞ # ‘e’ is short for end node. for (e = 0; e < N; e = e + 1): if e == S || e == next || notIn(e, subset)): continue newDistance = memo[e][state] + m[e][next] if (newDistance < minDist): minDist = newDistance memo[next][subset] = minDist # Returns true if the ith bit in ‘subset’ is not set function notIn(i, subset): return ((1 << i) & subset) == 0
  • 1058.
    # Generate allbit sets of size n with r bits set to 1. function combinations(r, n): subsets = [] combinations(0, 0, r, n, subsets) return subsets # Recursive method to generate bit sets. function combinations(set, at, r, n, subsets): if r == 0: subsets.add(set) else: for (i = at; i < n; i = i + 1): # Flip on ith bit set = set | (1 << i) combinations(set, i + 1, r - 1, n, subsets) # Backtrack and flip off ith bit set = set & ~(1 << i) NOTE: For a more detailed explanation on generating combinations see video “Backtracking tutorial: power set”
  • 1059.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1060.
    function findMinCost(m, memo,S, N): # The end state is the bit mask with # N bits set to 1 (equivalently 2N - 1) END_STATE = (1 << N) - 1 minTourCost = +∞ for (e = 0; e < N; e = e + 1): if e == S: continue tourCost = memo[e][END_STATE] + m[e][S] if tourCost < minTourCost: minTourCost = tourCost return minTourCost
  • 1061.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1062.
    function findOptimalTour(m, memo,S, N): lastIndex = S state = (1 << N) - 1; # End state tour = array of size N+1 for (i = N-1; i >= 1; i--): index = -1 for (j = 0; j < N; j++): if j == S || notIn(j, state): continue if (index == -1) index = j prevDist = memo[index][state] + m[index]lastIndex] newDist = memo[j][state] + m[j][lastIndex]; if (newDist < prevDist) index = j tour[i] = index state = state ^ (1 << index) lastIndex = index tour[0] = tour[N] = S return tour
  • 1063.
    # Finds theminimum TSP tour cost. # m - 2D adjacency matrix representing graph # S - The start node (0 ≤ S < N) function tsp(m, S): N = matrix.size # Initialize memo table. # Fill table with null values or +∞ memo = 2D table of size N by 2N setup(m, memo, S, N) solve(m, memo, S, N) minCost = findMinCost(m, memo, S, N) tour = findOptimalTour(m, memo, S, N) return (minCost, tour)
  • 1064.
    Next video: SourceCode! Implementation source code can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 1066.
    Traveling Salesman Problem Dynamic Programming 04 1 9 3 0 6 11 4 1 0 2 6 5 -4 0 A C B D
  • 1067.
  • 1068.
  • 1069.
    Existence of Eulerian Paths andCircuits William Fiset
  • 1070.
    What is anEulerian Path? An Eulerian Path (or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once.
  • 1071.
    An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1072.
    S We can findan Eulerian path on the following graph, but only if we start at specific nodes. An Eulerian Path (or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1073.
    S An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path? We can find an Eulerian path on the following graph, but only if we start at specific nodes.
  • 1074.
    S S An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path? We can find an Eulerian path on the following graph, but only if we start at specific nodes.
  • 1075.
    S S An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path? We can find an Eulerian path on the following graph, but only if we start at specific nodes.
  • 1076.
    S S An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path? We can find an Eulerian path on the following graph, but only if we start at specific nodes.
  • 1077.
    S S An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path? We can find an Eulerian path on the following graph, but only if we start at specific nodes.
  • 1078.
    An Eulerian Path(or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1079.
    S Suppose we startanother path but this time at a different node. An Eulerian Path (or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1080.
    S Suppose we startanother path but this time at a different node. An Eulerian Path (or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1081.
    S Choosing the wrong startingnode can lead to having unreachable edges. An Eulerian Path (or Eulerian Trail) is a path of edges that visits all the edges in a graph exactly once. What is an Eulerian Path?
  • 1082.
    What is anEulerian circuit? Similarly, an Eulerian circuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex.
  • 1083.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit?
  • 1084.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. If you know the graph contains an Eulerian cycle then you can start anywhere. s+e What is an Eulerian circuit?
  • 1085.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1086.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1087.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1088.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1089.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1090.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1091.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1092.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1093.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1094.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1095.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. s+e What is an Eulerian circuit?
  • 1096.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph. What is an Eulerian circuit?
  • 1097.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit? If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph.
  • 1098.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit? If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph.
  • 1099.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit? If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph.
  • 1100.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit? If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph.
  • 1101.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. What is an Eulerian circuit? If your graph does not contain an Eulerian cycle then you may not be able to return to the start node or you will not be able to visit all edges of the graph.
  • 1102.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. Oops, we’re stuck and can’t make it back to start node What is an Eulerian circuit?
  • 1103.
    Similarly, an Euleriancircuit (or Eulerian cycle) is an Eulerian path which starts and ends on the same vertex. There are also unvisited edges remaining Oops, we’re stuck and can’t make it back to start node What is an Eulerian circuit?
  • 1104.
    What conditions arerequired for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of the Euler path/circuit problem we care about: Eulerian Circuit Eulerian Path Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees.
  • 1105.
  • 1106.
    Node Degrees Undirected graph Thedegree of a node is how many edges are attached to it. Node degree = 3
  • 1107.
    Node Degrees Undirected graph Thedegree of a node is how many edges are attached to it. Directed graph Node degree = 3 In degree = 2 Out degree = 1 The indegree is the number of incoming edges and outdegree is number of outgoing edges.
  • 1108.
    What conditions arerequired for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of Euler path/circuit problem we care about: Eulerian Circuit Eulerian Path Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees.
  • 1109.
    Eulerian Circuit EulerianPath Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees. What conditions are required for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of Euler path/circuit problem we care about:
  • 1110.
    Eulerian Circuit EulerianPath Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees. What conditions are required for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of Euler path/circuit problem we care about:
  • 1111.
    Eulerian Circuit EulerianPath Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees. What conditions are required for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of Euler path/circuit problem we care about:
  • 1112.
    Eulerian Circuit EulerianPath Undirected Graph Every vertex has an even degree. Either every vertex has even degree or exactly two vertices have odd degree. Directed Graph Every vertex has equal indegree and outdegree At most one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees. What conditions are required for a valid Eulerian Path/Circuit? That depends on what kind of graph you’re dealing with. Altogether there are four flavors of Euler path/circuit problem we care about:
  • 1113.
    Does this undirectedgraph have an Eulerian path/circuit?
  • 1114.
    No Eulerian pathor circuit. There are too many nodes that have an odd degree. Does this undirected graph have an Eulerian path/circuit?
  • 1115.
    Does this undirectedgraph have an Eulerian path/circuit?
  • 1116.
    S/E S/E Only Eulerian path. Startand End nodes Does this undirected graph have an Eulerian path/circuit?
  • 1117.
    Does this undirectedgraph have an Eulerian path/circuit?
  • 1118.
    Yes! It hasboth an Eulerian path and circuit. Does this undirected graph have an Eulerian path/circuit?
  • 1119.
    True or false:if a graph has an Eulerian circuit, it also has an Eulerian path. Does this undirected graph have an Eulerian path/circuit?
  • 1120.
    Does this undirectedgraph have an Eulerian path/circuit? True or false: if a graph has an Eulerian circuit, it also has an Eulerian path.
  • 1121.
    Does this undirectedgraph have an Eulerian path/circuit?
  • 1122.
    There are noEulerian paths/circuits. An additional requirement when finding paths/ circuits is that all vertices with nonzero degree need to belong to a single connected component. Does this undirected graph have an Eulerian path/circuit?
  • 1123.
    Does this directedgraph have an Eulerian path/circuit?
  • 1124.
    Yes, it hasboth an Eulerian path and an Eulerian circuit because all in/out degrees are equal. Does this directed graph have an Eulerian path/circuit?
  • 1125.
    Does this directedgraph have an Eulerian path/circuit?
  • 1126.
    No path orcircuit. The red nodes have either too many in coming or outgoing edges. Does this directed graph have an Eulerian path/circuit?
  • 1127.
    Does this directedgraph have an Eulerian path/circuit?
  • 1128.
    S E This graph hasan Eulerian path, but no Eulerian circuit. It also has a unique start/end node for the path. Does this directed graph have an Eulerian path/circuit?
  • 1129.
    S E Note that thesingleton node has no incoming/ outgoing edges, so it doesn’t impact whether or not we have an Eulerian path. Does this directed graph have an Eulerian path/circuit?
  • 1130.
    Next Video: Eulerianpath algorithm
  • 1132.
  • 1133.
  • 1134.
  • 1135.
  • 1136.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6
  • 1137.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Step 1 to finding an Eulerian path is determining if there even exists an Eulerian path.
  • 1138.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Recall that for an Eulerian path to exist at most one one vertex has (outdegree) - (indegree) = 1 and at most one vertex has (indegree) - (outdegree) = 1 and all other vertices have equal in and out degrees. Step 1 to finding an Eulerian path is determining if there even exists an Eulerian path.
  • 1139.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 2 3 4 5 6 Count the in/out degrees of each node by looping through all the edges.
  • 1140.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 1 3 4 5 6
  • 1141.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 2 2 1 3 1 4 5 6
  • 1142.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 2 1 3 1 1 4 5 6
  • 1143.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 2 2 3 1 2 4 5 6
  • 1144.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 2 2 1 3 1 2 4 1 5 6
  • 1145.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 2 3 2 3 1 2 4 1 5 6
  • 1146.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 1 1 2 2 3 2 3 1 2 4 1 5 6 And so on for all other edges…
  • 1147.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1148.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 Once we’ve verified that no node has too many outgoing edges (out[i] - in[i] > 1) or incoming edges (in[i] - out[i] > 1) and there are just the right amount of start/end nodes we can be certain that an Eulerian path exists. The next step is to find a valid starting node.
  • 1149.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 Node 1 is the only node with exactly one extra outgoing edge, so it’s our only valid start node. Similarly, node 6 is the only node with exactly one extra incoming edge, so it will end up being the end node. out[1] - in[1] = 2 - 1 = 1 in[6] - out[6] = 2 - 1 = 1 Start node
  • 1150.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 NOTE: If all in and out degrees are equal (Eulerian circuit case) then any node with non-zero degree would serve as a suitable starting node.
  • 1151.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 Now that we know the starting node, let’s find an Eulerian path!
  • 1152.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 Let’s see what happens if we do a naive DFS, trying to traverse as many edges as possible until we get stuck.
  • 1153.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1154.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1155.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1156.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1157.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1158.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1159.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1160.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1
  • 1161.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 By randomly selecting edges during the DFS we made it from the start node to the end node. However, we did not find an Eulerian path because we didn’t traverse all the edges in our graph!
  • 1162.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 The good news is we can modify our DFS to handle forcing the traversal of all edges :)
  • 1163.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 To illustrate this, consider starting at node 0 and trying to find an Eulerian path.
  • 1164.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 To illustrate this, consider starting at node 0 and trying to find an Eulerian path.
  • 1165.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 To illustrate this, consider starting at node 0 and trying to find an Eulerian path.
  • 1166.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Whoops… we skipped the edges going to node 2 and back which need to be part of the solution.
  • 1167.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1168.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1169.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [3, 4] When backtracking, if the current node has any remaining unvisited edges (white edges) we follow any of them calling our DFS method recursively to extend the Eulerian path.
  • 1170.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [3, 4]
  • 1171.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [3, 4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1172.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [1, 3, 4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1173.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [2, 1, 3, 4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1174.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [1, 2, 1, 3, 4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1175.
    Finding an Eulerianpath (directed graph) 0 1 3 4 2 Solution: [0, 1, 2, 1, 3, 4] Once we get stuck (meaning the current node has no unvisited outgoing edges), we backtrack and add the current node to the solution.
  • 1176.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 Coming back to the previous example, let’s restart the algorithm, but this time track the number of unvisited edges we have left to take for each node.
  • 1177.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node In Out 0 0 0 1 1 2 2 3 3 3 3 3 4 2 2 5 1 1 6 2 1 In fact, we have already computed the number of outgoing edges for each edge in the “out” array which we can reuse. We won’t be needing the “in” array after we’ve validated that an Eulerian path exists.
  • 1178.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 2 2 3 3 3 4 2 5 1 6 1
  • 1179.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 2 2 3 3 3 4 2 5 1 6 1 Solution = []
  • 1180.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 3 3 3 4 2 5 1 6 1 Every time an edge is taken, reduce the outgoing edge count in the out array. Solution = []
  • 1181.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 3 3 2 4 2 5 1 6 1 Solution = []
  • 1182.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 3 3 2 4 2 5 0 6 1 Solution = []
  • 1183.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 3 3 2 4 2 5 0 6 0 Solution = []
  • 1184.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 3 3 1 4 2 5 0 6 0 Solution = []
  • 1185.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 2 3 1 4 2 5 0 6 0 Solution = []
  • 1186.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 1 2 2 3 1 4 1 5 0 6 0 Solution = []
  • 1187.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [] Node Out 0 0 1 1 2 2 3 1 4 1 5 0 6 0 When the DFS is stuck, meaning there are no more outgoing edges (i.e out[i] = 0), then we know to backtrack and add the current node to the solution.
  • 1188.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 1 2 2 3 1 4 1 5 0 6 0 When backtracking, if the current node has any remaining unvisited edges (white edges), we follow any of them, calling our DFS method recursively to extend the Eulerian path. We can verify there still are outgoing edges by checking if out[i] != 0.
  • 1189.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 1 2 2 3 1 4 0 5 0 6 0
  • 1190.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 1 2 2 3 0 4 0 5 0 6 0
  • 1191.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 0 2 2 3 0 4 0 5 0 6 0
  • 1192.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 0 2 1 3 0 4 0 5 0 6 0
  • 1193.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [6] Node Out 0 0 1 0 2 1 3 0 4 0 5 0 6 0 When the DFS is stuck, meaning there are no more outgoing edges (i.e out[i] = 0), then we know to backtrack and add the current node to the solution.
  • 1194.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [4,6] Node Out 0 0 1 0 2 1 3 0 4 0 5 0 6 0 Node 2 still has an unvisited edge (since out[i] != 0) so we need to follow that edge.
  • 1195.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [4,6] Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 When the DFS is stuck, meaning there are no more outgoing edges (i.e out[i] = 0), then we know to backtrack and add the current node to the solution.
  • 1196.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [2,4,6] Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0
  • 1197.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Solution = [2,2,4,6] Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0
  • 1198.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [1,2,2,4,6]
  • 1199.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [3,1,2,2,4,6]
  • 1200.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [4,3,1,2,2,4,6]
  • 1201.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [2,4,3,1,2,2,4,6]
  • 1202.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [3,2,4,3,1,2,2,4,6]
  • 1203.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [6,3,2,4,3,1,2,2,4,6]
  • 1204.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [5,6,3,2,4,3,1,2,2,4,6]
  • 1205.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [3,5,6,3,2,4,3,1,2,2,4,6]
  • 1206.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [1,3,5,6,3,2,4,3,1,2,2,4,6]
  • 1207.
    Finding an Eulerianpath (directed graph) 0 1 2 3 4 5 6 Node Out 0 0 1 0 2 0 3 0 4 0 5 0 6 0 Solution = [1,3,5,6,3,2,4,3,1,2,2,4,6] The time complexity to find an eulerian path with this algorithm is O(E). The two calculations we’re doing: computing in/out degrees + DFS are both linear in the number of edges.
  • 1208.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1209.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1210.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1211.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1212.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1213.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1214.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1215.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1216.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1217.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1218.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1219.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1220.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1221.
    function countInOutDegrees(): for edgesin g: for edge in edges: out[edge.from]++ in[edge.to]++ function graphHasEulerianPath(): start_nodes, end_nodes = 0, 0 for (i = 0; i < n; i++): if (out[i] - in[i]) > 1 or (in[i] - out[i]) > 1: return false else if out[i] - in[i] == 1: start_nodes++ else if in[i] - out[i] == 1: end_nodes++ return (end_nodes == 0 and start_nodes == 0) or (end_nodes == 1 and start_nodes == 1)
  • 1222.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1223.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1224.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1225.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1226.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1227.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1228.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1229.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at) Avoids starting DFS at a singleton
  • 1230.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1231.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1232.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1233.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1234.
    function dfs(at): # Whilethe current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at) The out array is currently serving two purposes. One purpose is to track whether or not there are still outgoing edges, and the other is to index into the adjacency list to select the next outgoing edge. This assumes the adjacency list stores edges in a data structure that is indexable in O(1) (e.g stored in an array). If not (e.g a linked-list/stack/etc…), you can use an iterator to iterate over the edges.
  • 1235.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1236.
    function findStartNode(): start =0 for (i = 0; i < n; i = i + 1): # Unique starting node if out[i] - in[i] == 1: return i # Start at any node with an outgoing edge if out[i] > 0: start = i return start function dfs(at): # While the current node still has outgoing edges while (out[at] != 0): # Select the next unvisited outgoing edge next_edge = g[at].get(——out[at]) dfs(next_edge.to) # Add current node to solution path.insertFirst(at)
  • 1237.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1238.
    # Global/class scopevariables n = number of vertices in the graph m = number of edges in the graph g = adjacency list representing directed graph in = [0, 0, …, 0, 0] # Length n out = [0, 0, …, 0, 0] # Length n path = empty integer linked list data structure function findEulerianPath(): countInOutDegrees() if not graphHasEulerianPath(): return null dfs(findStartNode()) # Return eulerian path if we traversed all the # edges. The graph might be disconnected, in which # case it’s impossible to have an euler path. if path.size() == m+1: return path return null
  • 1239.
    Source Code Link Implementationsource code can be found at the following link: github.com/williamfiset/algorithms Link in the description:
  • 1241.
  • 1242.
  • 1243.
  • 1244.
  • 1245.
    What is aMinimum Spanning Tree? Given an undirected graph with weighted edges, a Minimum Spanning Tree (MST) is a subset of the edges in the graph which connects all vertices together (without creating any cycles) while minimizing the total edge cost.
  • 1246.
    What is aMinimum Spanning Tree? 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 Given an undirected graph with weighted edges, a Minimum Spanning Tree (MST) is a subset of the edges in the graph which connects all vertices together (without creating any cycles) while minimizing the total edge cost.
  • 1247.
    What is aMinimum Spanning Tree? MST cost: 0 + 5 + -2 + 2 + 1 + 3 = 9 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 This particular graph has a unique MST highlighted in green. However, it is common for a graph to have multiple valid MSTs of equal costs.
  • 1248.
  • 1249.
  • 1250.
    1 0 4 3 Find any MST 2 5 7 68 1 2 3 4 5 6 7 8 9 10 11 12
  • 1251.
    1 0 4 3 Find any MST 2 5 7 68 1 2 3 4 5 6 7 8 9 10 11 12 1 0 4 3 2 5 7 6 8 1 2 3 4 5 6 7 8 9 10 11 12 MST cost: 3 + 1 + 2 + 4 + 7 + 8 + 9 + 5 = 39
  • 1252.
  • 1253.
    This graph hasno MST! All nodes must be connected to form a spanning tree. 1 0 3 2 5 4 1 1 -5 10 1 10 Find any MST
  • 1254.
    Prim’s MST Algorithm Prim’sis a greedy MST algorithm that works well on dense graphs. On these graphs, Prim’s meets or improves on the time bounds of its popular rivals (Kruskal’s & Borůvka’s). However, when it comes to finding the minimum spanning forest on a disconnected graph, Prim’s cannot do this as easily (the algorithm must be run on each connected component individually). The lazy version of Prim’s has a runtime of O(E*log(E)), but the eager version (which we will also look at) has a better runtime of O(E*log(V)).
  • 1255.
    Lazy Prim’s MSTOverview Start the algorithm on any node s. Mark s as visited and iterate over all edges of s, adding them to the PQ. Maintain a min Priority Queue (PQ) that sorts edges based on min edge cost. This will be used to determine the next node to visit and the edge used to get there. While the PQ is not empty and a MST has not been formed, dequeue the next cheapest edge from the PQ. If the dequeued edge is outdated (meaning the node it points to has already been visited) then skip it and poll again. Otherwise, mark the current node as visited and add the selected edge to the MST. Iterate over the new current node’s edges and add all its edges to the PQ. Do not add edges to the PQ which point to already visited nodes.
  • 1256.
  • 1257.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 One thing to bear in mind is that although the graph above represents an undirected graph, the internal adjacency list representation typically has each undirected edge stored as two directed edges.
  • 1258.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 The actual internal representation typically looks like this.
  • 1259.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 On the right we will be keeping track of the PQ containing the edge objects as triplets: (start node, end node, edge cost) Edges in PQ (start, end, cost)
  • 1260.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 Unvisited Visiting Visited Edges in PQ (start, end, cost)
  • 1261.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 Let’s begin Prim’s at node 0. Edges in PQ (start, end, cost)
  • 1262.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 Iterate over all outgoing edges and add them to the PQ. Edges in PQ (start, end, cost)
  • 1263.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) Edges in PQ (start, end, cost)
  • 1264.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) Edges in PQ (start, end, cost)
  • 1265.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) Edges in PQ (start, end, cost)
  • 1266.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) Edges in PQ (start, end, cost)
  • 1267.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) Edges in PQ (start, end, cost)
  • 1268.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) Edge (0, 2, 1) has the lowest value in the PQ so it gets added to the MST. This also means that the next node we process is node 2. Edges in PQ (start, end, cost)
  • 1269.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) Edges in PQ (start, end, cost)
  • 1270.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) Next, iterate through all the edges of node 2 and add them to the PQ. Edges in PQ (start, end, cost)
  • 1271.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) Edges in PQ (start, end, cost)
  • 1272.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) Edges in PQ (start, end, cost)
  • 1273.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) Node 0 is already visited so we skip adding the edge (2, 0, 1) to the PQ. Edges in PQ (start, end, cost)
  • 1274.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) The reason we don't include edges which point to already visited nodes is that either they overlap with an edge already part of the MST (as is the case with the edge on this slide) or it would introduce a cycle in the MST, if included. Edges in PQ (start, end, cost)
  • 1275.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) Edges in PQ (start, end, cost)
  • 1276.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) Edge (2, 3, 2) has the lowest value in the PQ so it gets added to the MST. This also means that the next node we process is node 3. Edges in PQ (start, end, cost)
  • 1277.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) Edges in PQ (start, end, cost)
  • 1278.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) The same process of adding edges to the PQ and polling the smallest edge continues until the MST is complete. Edges in PQ (start, end, cost)
  • 1279.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) Edges in PQ (start, end, cost)
  • 1280.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1281.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1282.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1283.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1284.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1285.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) Edges in PQ (start, end, cost)
  • 1286.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) Edges in PQ (start, end, cost)
  • 1287.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1288.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1289.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1290.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1291.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1292.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1293.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1294.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1295.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1296.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1297.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1298.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1299.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1300.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1301.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1302.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1303.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1304.
    (2, 1, 3) LazyPrim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 2 2 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 The edge (2, 1, 3) is stale, poll again. (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1305.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 The edge (0, 3, 4) is also stale, poll again. (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1306.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1307.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1308.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1309.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1310.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1311.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1312.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 The edge (3, 6, 7) is also stale, poll again. (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1313.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 There’s a tie between (2, 5, 8) and (4, 7, 8) for which edge gets polled next. Since (2, 5, 8) was added first, let’s assume it gets priority (it doesn’t matter in practice). (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1314.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 The edge (2, 5, 8) is stale, poll again. (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1315.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) (2, 5, 8) Edges in PQ (start, end, cost)
  • 1316.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1317.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1318.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1319.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1320.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1321.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0 We can now stop Prim’s since the MST is complete. We know the MST is complete because the number of edges in the tree is one less than the number of nodes in the graph (i.e. the definition of a tree). (6, 7, 12) (4, 7, 8) (4, 1, 0) (0, 1, 10) (0, 2, 1) (0, 3, 4) (2, 1, 3) (2, 5, 8) (2, 3, 2) (3, 5, 2) (3, 6, 7) (5, 4, 1) (5, 6, 6) (5, 7, 9) Edges in PQ (start, end, cost)
  • 1322.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 If we collapse the graph back into the undirected edge view it becomes clear which edges are included in the MST.
  • 1323.
  • 1324.
    Lazy Prim’s 1 0 2 3 4 5 6 7 10 1 3 12 4 22 7 8 0 1 8 9 6 The MST cost is: 1 + 2 + 2 + 6 + 1 + 0 + 8 = 20
  • 1325.
    Lazy Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. pq = … # PQ data structure; stores edge objects consisting of # {start node, end node, edge cost} tuples. The PQ sorts # edges based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1326.
    Lazy Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. pq = … # PQ data structure; stores edge objects consisting of # {start node, end node, edge cost} tuples. The PQ sorts # edges based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1327.
    Lazy Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. pq = … # PQ data structure; stores edge objects consisting of # {start node, end node, edge cost} tuples. The PQ sorts # edges based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1328.
    Lazy Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. pq = … # PQ data structure; stores edge objects consisting of # {start node, end node, edge cost} tuples. The PQ sorts # edges based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1329.
    Lazy Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. pq = … # PQ data structure; stores edge objects consisting of # {start node, end node, edge cost} tuples. The PQ sorts # edges based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1330.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1331.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1332.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1333.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1334.
    function addEdges(nodeIndex): # Markthe current node as visited. visited[nodeIndex] = true # Iterate over all edges going outwards from the current node. # Add edges to the PQ which point to unvisited nodes. edges = g[nodeIndex] for (edge : edges): if !visited[edge.to]: pq.enqueue(edge) Helper method to iterate over the edges of a node and add edges to the PQ:
  • 1335.
    function addEdges(nodeIndex): # Markthe current node as visited. visited[nodeIndex] = true # Iterate over all edges going outwards from the current node. # Add edges to the PQ which point to unvisited nodes. edges = g[nodeIndex] for (edge : edges): if !visited[edge.to]: pq.enqueue(edge) Helper method to iterate over the edges of a node and add edges to the PQ:
  • 1336.
    function addEdges(nodeIndex): # Markthe current node as visited. visited[nodeIndex] = true # Iterate over all edges going outwards from the current node. # Add edges to the PQ which point to unvisited nodes. edges = g[nodeIndex] for (edge : edges): if !visited[edge.to]: pq.enqueue(edge) Helper method to iterate over the edges of a node and add edges to the PQ:
  • 1337.
    function addEdges(nodeIndex): # Markthe current node as visited. visited[nodeIndex] = true # Iterate over all edges going outwards from the current node. # Add edges to the PQ which point to unvisited nodes. edges = g[nodeIndex] for (edge : edges): if !visited[edge.to]: pq.enqueue(edge) Helper method to iterate over the edges of a node and add edges to the PQ:
  • 1338.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1339.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1340.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1341.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges) Skip edge that points to a visited node.
  • 1342.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1343.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1344.
    # s -the index of the starting node (0 ≤ s < n) function lazyPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m addEdges(s) while (!pq.isEmpty() and edgeCount != m): edge = pq.dequeue() nodeIndex = edge.to if visited[nodeIndex]: continue mstEdges[edgeCount++] = edge mstCost += edge.cost addEdges(nodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1345.
  • 1346.
    Prim’s Minimum SpanningTree Algorithm (lazy version) 1 0 2 3 4 5 6 7 10 1 3 12 4 2 2 7 8 0 1 8 9 6 10 3 1 4 2 2 8 7 6 12 9 8 1 0
  • 1347.
  • 1348.
  • 1349.
    Previous Video: LazyPrim’s MST <insert video clip> Link to lazy Prim’s in the description
  • 1350.
    The lazy implementationof Prim’s inserts up to E edges into the PQ. Therefore, each poll operation on the PQ is O(log(E)). Eager Prim’s Instead of blindly inserting edges into a PQ which could later become stale, the eager version of Prim’s tracks (node, edge) key-value pairs that can easily be updated and polled to determine the next best edge to add to the MST.
  • 1351.
    Key realization: forany MST with directed edges, each node is paired with exactly one of its incoming edges (except for the start node). Eager Prim’s This can easily be seen on a directed MST where you can have multiple edges leaving a node, but at most one edge entering a node. Let’s take a closer look…
  • 1352.
  • 1353.
  • 1354.
  • 1355.
    Eager Prim’s 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 74 6 3 0 Looking at the directed MST, you can see that each node is paired with an incoming edge except for the starting node.
  • 1356.
    Eager Prim’s 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 74 6 3 0 Looking at the directed MST, you can see that each node is paired with an incoming edge except for the starting node.
  • 1357.
    Eager Prim’s A slightdifference from the lazy version is that instead of adding edges to the PQ as we iterate over the edges of node, we’re going to relax (update) the destination node’s most promising incoming edge. In the eager version of Prim’s we are trying to determine which of a node’s incoming edges we should select to include in the MST.
  • 1358.
    Eager Prim’s A naturalquestion to ask at this point is how are we going to efficiently update and retrieve these (node, edge) pairs? One possible solution is to use an Indexed Priority Queue (IPQ) which can efficiently update and poll key-value pairs. This reduces the overall time complexity from O(E*logE) to O(E*logV) since there can only be V (node, edge) pairs in the IPQ, making the update and poll operations O(logV).
  • 1359.
    Indexed Priority QueueDS Video <insert video clip> Link to IPQ video the description
  • 1360.
    Eager Prim’s Algorithm Startthe algorithm on any node 's'. Mark s as visited and relax all edges of s. Maintain a min Indexed Priority Queue (IPQ) of size V that sorts vertex-edge pairs (v, e) based on the min edge cost of e. By default, all vertices v have a best value of (v, ∅) in the IPQ. While the IPQ is not empty and a MST has not been formed, dequeue the next best (v, e) pair from the IPQ. Mark node v as visited and add edge e to the MST. Next, relax all edges of v while making sure not to relax any edge pointing to a node which has already been visited. relaxing in this context refers to updating the entry for node v in the IPQ from (v, eold) to (v, enew) if the new edge enew from u -> v has a lower cost than eold.
  • 1361.
  • 1362.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 Onething to bear in mind is that while the graph above represents an undirected graph, the internal adjacency list representation typically has each undirected edge stored as two directed edges.
  • 1363.
  • 1364.
    (node, edge) key-value pairsin IPQ 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0
  • 1365.
    node index ->(start node, end node, edge cost) 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node, edge) key-value pairs in IPQ 2 -> (0, 2, 0)
  • 1366.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7)
  • 1367.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5)
  • 1368.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1369.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1370.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9) The next cheapest (node, edge) pair is 2 -> (0, 2, 0).
  • 1371.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1372.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9) Ignore edges pointing to already-visited nodes.
  • 1373.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (0, 5, 7) 3 -> (0, 3, 5) 1 -> (0, 1, 9) Edge (2, 5, 6) has a lower cost going to node 5 so update the IPQ with the new edge.
  • 1374.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (0, 1, 9) Edge (2, 5, 6) has a lower cost going to node 5 so update the IPQ with the new edge.
  • 1375.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1376.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1377.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1378.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (0, 1, 9)
  • 1379.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (3, 1, -2)
  • 1380.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (2, 5, 6) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1381.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1382.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1383.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1384.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1385.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1386.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1387.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3)
  • 1388.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1389.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1390.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1391.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1392.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1393.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1394.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (3, 6, 3) 4 -> (1, 4, 3)
  • 1395.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1396.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1397.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1398.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1399.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1400.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1401.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1402.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1403.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1404.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1405.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1406.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1407.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1408.
    0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 (node,edge) key-value pairs in IPQ 2 -> (0, 2, 0) 5 -> (3, 5, 2) 3 -> (0, 3, 5) 1 -> (3, 1, -2) 6 -> (5, 6, 1) 4 -> (1, 4, 3)
  • 1409.
    0 2 5 3 1 6 4 9 4 1 2 0 7 -2 6 5 3 3 6 If wecollapse the graph back into the undirected edge view it becomes clear which edges are included in the MST.
  • 1410.
    0 2 5 3 1 6 4 9 4 1 2 0 7 -2 6 5 3 3 6 The MSTcost is: 0 + 5 + 2 + 1 + -2 + 3 = 9
  • 1411.
    n = …# Number of nodes in the graph. ipq = … # IPQ data structure; stores (node index, edge object) # pairs. The edge objects consist of {start node, end # node, edge cost} tuples. The IPQ sorts (node index, # edge object) pairs based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n Eager Prim’s Pseudo Code Let’s define a few variables we’ll need:
  • 1412.
    Eager Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. ipq = … # IPQ data structure; stores (node index, edge object) # pairs. The edge objects consist of {start node, end # node, edge cost} tuples. The IPQ sorts (node index, # edge object) pairs based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1413.
    Eager Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. ipq = … # IPQ data structure; stores (node index, edge object) # pairs. The edge objects consist of {start node, end # node, edge cost} tuples. The IPQ sorts (node index, # edge object) pairs based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1414.
    Eager Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. ipq = … # IPQ data structure; stores (node index, edge object) # pairs. The edge objects consist of {start node, end # node, edge cost} tuples. The IPQ sorts (node index, # edge object) pairs based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1415.
    Graph edge densityanalysis Y-axis: time required to find MST in milliseconds. X-axis: Edge density percentage on graph with 5000 nodes. 10% 20% 30% 40% 50% 60% 70% 80% 90% 125ms 250ms 375ms Adjacency List Adjacency Matrix
  • 1416.
    Eager Prim’s PseudoCode Let’s define a few variables we’ll need: n = … # Number of nodes in the graph. ipq = … # IPQ data structure; stores (node index, edge object) # pairs. The edge objects consist of {start node, end # node, edge cost} tuples. The IPQ sorts (node index, # edge object) pairs based on min edge cost. g = … # Graph representing an adjacency list of weighted edges. # Each undirected edge is represented as two directed # edges in g. For especially dense graphs, prefer using # an adjacency matrix instead of an adjacency list to # improve performance. visited = [false, …, false] # visited[i] tracks whether node i # has been visited; size n
  • 1417.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1418.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1419.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1420.
    function relaxEdgesAtNode(currentNodeIndex): # Markthe current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge) Helper method
  • 1421.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1422.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1423.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1424.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1425.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1426.
    function relaxEdgesAtNode(currentNodeIndex): # Markthe current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge) Helper method
  • 1427.
    function relaxEdgesAtNode(currentNodeIndex): # Markthe current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge) Helper method
  • 1428.
    Helper method function relaxEdgesAtNode(currentNodeIndex): #Mark the current node as visited. visited[currentNodeIndex] = true # Get all the edges going outwards from the current node. edges = g[currentNodeIndex] for (edge : edges): destNodeIndex = edge.to # Skip edges pointing to already visited nodes. if visited[destNodeIndex]: continue if !ipq.contains(destNodeIndex): # Insert edge for the first time. ipq.insert(destNodeIndex, edge) else: # Try and improve the cheapest edge at destNodeIndex with # the current edge in the IPQ. ipq.decreaseKey(destNodeIndex, edge)
  • 1429.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1430.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1431.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1432.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1433.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1434.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1435.
    # s -the index of the starting node (0 ≤ s < n) function eagerPrims(s = 0): m = n - 1 # number of edges in MST edgeCount, mstCost = 0, 0 mstEdges = [null, …, null] # size m relaxEdgesAtNode(s) while (!ipq.isEmpty() and edgeCount != m): # Extract the next best (node index, edge object) # pair from the IPQ destNodeIndex, edge = ipq.dequeue() mstEdges[edgeCount++] = edge mstCost += edge.cost relaxEdgesAtNode(destNodeIndex) if edgeCount != m: return (null, null) # No MST exists! return (mstCost, mstEdges)
  • 1436.
    Next Video: eagerPrim’s source code
  • 1437.
    Prim’s Minimum SpanningTree 0 2 5 3 1 6 4 9 4 1 2 3 0 7 6 -2 6 5 3 9 -2 5 2 3 1 6 7 4 6 3 0 Looking at the directed MST, you can see that each node is paired with an incoming edge except for the starting node.
  • 1439.
  • 1440.