Graph Algorithms
For an intro to graphs refer to this page for more details.
Exploring Undirected Graph
Let be an undirected graph.
Task: Find all vertices that can be reached from a given vertex .
def explore(v):visited(v) = Trueprevisit(v) # see laterfor each edge {u,v} in E do:if visited(u) = False:call explore(u)postvisit(v) # see later
The number of vertices u such that “visited(u) = false” decreases in each recursive call. Since there is a finite number of vertices, the algorithm eventually terminates.
Lemma
Assume that, initially, visited(u) = False. After explore(v) has terminated, visited(u) = True there is a path from to .
Connected Components of G = (V, E)
def DFS (G):for all v in V do:visited (v) = falsecc = 0for all v in V do:if visited (v) = false:cc = cc + 1explore (v)
Run-time of DFS
First for-loop: time
Second for-loop:
explore(u)
is called exactly once for each vertex u (this may be a recursive call)- time spent for
explore(u)
, excluding recursive calls is
Total time:
Directed Graphs
Assume that is directed and acyclic.
Topological Sorting or Ordering is the numbering the vertices such that for each edge
def TopologicalOrdering(G):# Input: a directed acyclic graph G (V, E)# Output: A topological ordering of Vk = 1while V != {} do:Choose a vertex u in V with indegree 0Give u the number kk = k + 1Remove u from G
Prenumbers and Postnumbers
Let be a directed graph. For each vertex , we define the following two numbers with respect to Depth-First-Search.
- pre(v): the first time we visit (the time at which explore(v) is called)
- post(v): the time at which explore(v) is finished.
Use variable clock. At start, clock = 1.
def previsit(v):pre(v) = clockclock += 1def postvisit(v):post(v) = clockclock += 1
4 Types of Edges
Tree edge
- edge
- is called as a recursive call within
- solid edge
- literally the edges we follow when we execute DFS
Forward edge
- edge where in the (solid) tree,
- is in subtree of
- is not a child of
- shortcut that brings you down the tree
Back edge
- edge where in the (solid) tree,
- in subtree of
- enable you to go back in the tree
Cross edge
- lets you go from one subtree to another
- all other edges
Acyclic vs Cyclic
has a directed cycle if and only if DFS-forest has a back-edge.
How to test if a directed graph is cyclic?
- Run DFS (including pre/post-numbers)
- For each non-tree edge(v, u), test if pre(u) < pre(v) > post(v) < post(u)
- if "yes" fpr at least one non-tree edge, return "cyclic"
- if "no" for all non-tree edges, return "acyclic"
Running time: O(|V| + |E|)
Topological Sort for directed acyclic graph
- Run DFS (including pre/post-numbers)
- Run Bucket Sort to sort the vertices by post-number.
- Obtain the topological ordering from the reverse sorted order of the post-numbers.
Shortest Paths
Dijkstra's Algorithm
Input:
- A directed graph where each edge has a weight
- A vertex called the source
Output:
- For each vertex , = length of a shortest path from to .
For each vertex , maintain variable = length of a shortest path from to found so far.
At start, if if
Loop: Pick a vertex u for which . For each edge
for each vertex v in V:d(v) = infinityd(s) = 0S = {}Q = Vwhile Q != {}:u = vertex in Q for which d(u) is minimumdelete u from Qinsert u into Sfor each edge (u,v):d(v) = min (d(v), d(u) + wt(u,v))
Example:
- Let and .
- Store in a min-heap, where the key of each vertex is .
- Initialization: (including the time to build the heap storing ).
- One iteration:
- Find and delete it from Q.
extract_min
: time.
- For each edge (u, v), we update
decrease_key
: time.
Total time for one iteration:
Total running time:
Dijkstra Correctness
Theorem
Let be a weighted directed graph and be a source vertex. Dijkstra algorithm finds the lengths of the shortest paths from to all vertices in .
We prove by induction that
- if a vertex is in , then gives the length of a shortest path from to and
- if a vertex is not in , then gives the length of a shortest special path from to .
Base case:
- At the beginning, , so (1) above is vacuously true.
- Since , the other vertices simply cannot be reached by following a special path from . Since is initialized to , then (2) above also holds when the algorithm starts.
Induction hypothesis: Assume that both (1) and (2) hold right before we add a new vertex v to S. We address induction steps (1) and (2) separately.
Induction step (1): By the induction hypothesis (1), before the addition of , we already know a shortest path from to all vertices that are in . Adding to does not change these shortest paths.
As for node , it is about to be inserted in . Before adding it to , we must check gives the length of a shortest path from to .
So we compare to the lengths of all paths from to . There are two kinds of paths:
- the paths that are special
- the ones that are not
By the induction hypothesis (2), we already know that is less than or equal to the length of any special path from to . A non-special path from to is one which contains at least one vertex that is not in . But such a path has length at least .
Hence, is less than or equal to the length of any path from to , which means that gives the length of a shortest path from to . So the induction step is complete for .
Induction step (2): Consider a node which is not in . Let be the last vertex in encountered on a shortest special path from to . We consider:
If then adding to does not change the value of . Hence, by the induction hypothesis (2), gives the length of a shortest special path from to .
If , then by doing , we ensure that after adding to , gives the length of a shortest special path from to .