Outline
- Depth First Search
- Quiz
- Topological Sort
- Minimal Spanning Trees
Introduction
- Last week, we started talking about graph representation and algorithms.
- We said a graph `G=(V, E)` could be represented either using an adjacency list representation or an adjacency-matrix representation.
- We then gave an algorithm for breadth-first search on a graph and showed an example.
- We argued that this algorithm had `O(|V| +|E|)` runtime.
- Our algorithm as part of its computation computed the node by which a given node got "discovered" and added to frontier queue.
- Let's take a look at how we could use this information to compute a path from a start node to a given vertex...
Shortest paths
The following code prints the shortest path from vertex `s` to vertex `v` in a graph `G` assuming BFS has already computed a breadth-first tree. I.e., so
each node `v` has its parent value `v.pi` set as described in the BFS algorithm.
PRINT-PATH(G, s, v)
1 if v == s
2 print s
3 elseif v.pi = NIL
4 print "no path from" s "to" v "exists"
5 else PRINT-PATH(G, s, v.pi)
6 print v
Depth First Search
- Depth-first search explores edges out of the most recently discovered vertex `v` that still has unexplored edges leaving it.
- Once all of `v`'s edges have been explored, the search "backtracks" to explore edges leaving the vertex from which `v` was discovered.
- The process continues until we explore all the vertices reachable from the original source vertex...
Depth First Pseudo-code
As with breadth first search, we keep track of node color and predecessor `pi`. We also make use of a global variable time to timestamp a vertex `v` for when it was discovered `v.d` and when it was finished `v.f` (its adjacency list has been completely examined).
DFS(G)
1 for each vertex u in G.V //initialization
2 u.color = WHITE (recall white means unexplored)
3 u.pi = nil
4 time = 0
5 for each vertex u in G.V
6 if u.color == WHITE
7 DFS-VISIT(G,u)
DFS-VISIT(G,u)
1 time = time + 1
2 u.d = time
3 u.color = GRAY
4 for each v in G.adj[u]
5 if v.color == WHITE
6 v.pi = u
7 DFS-VISIT(G, v) // recursion means we're using a stack implicitly
8 u.color = BLACK
9 time = time + 1
10 u.f = time
DFS Example
- The above sequence of images illustrate the progress of depth first search on a six node graph.
- White means unexplored, gray indicates a node has been discovered but not all of its children have been explored, black means all of the children of a node have been explored.
- The numbers of the form `d`/`f` in a node indicate discover (d) and finishing times (f).
- Dotted edges indicating B, F, C on an edge denote a back edge (an edge connecting a vertex `u` to an ancestor in a depth first tree), a forward edge (an edge connecting a vertex `u` to a descendent in a depth-first tree), and cross edges (vertices that are nontree but are neither back or forward)
- We might get cross edges when we explore one connected component of the graph then in line 5 of DFS cycle to a new vertex in `G.V`.
- Notice we are using color to prevent getting into a loop because of back-edges.
DFS Run-time
- Lines 1-3 and 5-7 to of DFS take `Theta(|V|)` time to execute excluding the calls to DFS-VISIT.
- The procedure DFS-VISIT is called exactly once for each vertex `v in V`, since the vertex `u` on which DFS-VISIT is invoked must be white
and the first thing DFS-VISIT does is paint vertex `u` gray.
- During an execution of DFS-VISIT(G, v), the loop on lines 4-7 executes |Adj[v]| times. Since
`sum_(v in V) |Adj[v]| = Theta(|E|)`,
the total cost of lines 4-7 is `Theta(|E|)`
- So the total run time is `Theta(|V| + |E|)`.
Quiz (Sec 5)
Which of the following statements is true?
- The run-time of the function LCS-LENGTH from last week only depended on the length of the longer sequence.
- The ACTIVITY-SELECTOR problem can be solved using a greedy algorithm.
- The breadth-first search algorithm from last week makes use of a stack.
Quiz (Sec 6)
Which of the following statements is true?
- The run-time of the function LCS-LENGTH from last week only depended on the length of the shorter sequence.
- The ACTIVITY-SELECTOR problem cannot be solved using Dynamic Programming.
- The breadth-first search algorithm from last week makes use of a queue.
Topological Sort
- A topological sort of a directed acyclic graph (dag) `G=(V, E)` is a linear ordering of all its vertices such that if `G` contains an edge `(u,v)`, then `u` appears before `v` in the ordering.
- If the graph had a cycle such an order would be possible.
- We can view a topological sort of a graph as an ordering of its vertices along a horizontal line so that all directed edges go from left to right.
- The above image illustrates an example of this.
- As indicated by the above example, topological sorting can be useful for scheduling and planning. (In the above, it might be important for ordering how to put clothes on).
Topological Sort Pseudo-code
We leverage our DFS code to give the following algorithm for topological sort:
TOPOLOGICAL-SORT(G)
1 call DFS(G) to compute finishing times v.f for each vertex v
2 as each vertex is finished, insert it onto the front of a linked list
3 return the linked list of vertices.
- Image (b) on the previous slide shows how the topologically sorted vertices appear in reverse order of their finishing times.
- The runtime of DFS is `Theta(|V| + |E|)` since this is the runtime of DFS and its takes `O(1)` time to insert each of the `|V|` vertices onto the front of
the linked list.
- Intuitively, DFS from a node `u` finds all of the vertices in the graph reachable by exploring forward edges from `u`. I.e., its DFS subtree. These nodes each must appear after `u` in any topological ordering.
Minimum Spanning Trees
- Electronic circuits often need to make the pins of several components equivalent by wiring them together. To interconnect `n` pins, we can use an arrangement of `n-1` wires, each connecting two pins.
- Of all such arrangements, the one that uses the least amount of wire is usually the most desirable.
- To model this problem, let `G=(V,E)` where `V` is the set of pins, `E` is the set of possible connections between pairs of pins, and for each edge `(u,v) in E`, we have a weight `w(u,v)` specifying the cost to connect `u` to `v`.
- We wish to find an acyclic subset `T subseteq E` that connects all of the vertices and whose total weight
`w(T) = sum_((u,v) in T)w(u,v)`
is minimized.
- Since `T` is acyclic (and the graph was undirected) and connects all of the vertices, it must form a tree, which we call a spanning tree.
- The problem just described is called the minimum spanning tree problem.
- The darkened edges give a MST for the above graph.
Pseudo-code for Generic MST
GENERIC-MST(G,w)
1 A = 0
2 while A does not form a spanning tree
3 find an edge (u,v) that is safe for A
4 A = A union {(u,v)}
5 return A
Next day we will explore how to find a safe edge.