22.3 Depth-first search
22.3-1
Make a -by- chart with row and column labels , , and . In each cell , indicate whether, at any point during a depth-first search of a directed graph, there can be an edge from a vertex of color to a vertex of color . For each possible edge, indicate what edge types it can be. Make a second such chart for depth-first search of an undirected graph.
According to Theorem 22.7 (Parenthesis theorem), there are 3 cases of relationship between intervals of vertex and :
- and are entirely disjointed,
- , and
- .
We judge the possibility according to this Theorem.
-
For directed graph, we can use the edge classification given by exercise 22.3-5 to simplify the problem.
-
For undirected graph, starting from directed chart, we remove the forward edge and the cross edge, and
- when a back edge exist, we add a tree edge;
- when a tree edge exist, we add a back edge.
This is correct for the following reasons:
- Theorem 22.10: In a depth-first search of an undirected graph , every edge of is either a tree or back edge. So tree and back edge only.
- If is a tree edge from 's perspective, is also a back edge from 's perspective.
22.3-2
Show how depth-first search works on the graph of Figure 22.6. Assume that the for loop of lines 5–7 of the procedure considers the vertices in alphabetical order, and assume that each adjacency list is ordered alphabetically. Show the discovery and finishing times for each vertex, and show the classification of each edge.
The following table gives the discovery time and finish time for each vetex in the graph.
See the C++ demo.
- Tree edges: , , , , , , , .
- Back edges: , , .
- Forward edges: .
- Cross edges: , .
22.3-3
Show the parenthesis structure of the depth-first search of Figure 22.4.
The parentheses structure of the depth-first search of Figure 22.4 is .
22.3-4
Show that using a single bit to store each vertex color suffices by arguing that the procedure would produce the same result if line 3 of was removed.
Change line 3 to color = BLACK
and remove line 8. Then, the algorithm would produce the same result.
22.3-5
Show that edge is
a. a tree edge or forward edge if and only if ,
b. a back edge if and only if , and
c. a cross edge if and only if .
a. is an ancestor of .
b. is a descendant of .
c. is visited before .
22.3-6
Show that in an undirected graph, classifying an edge as a tree edge or a back edge according to whether or is encountered first during the depth-first search is equivalent to classifying it according to the ordering of the four types in the classification scheme.
By Theorem 22.10, every edge of an undirected graph is either a tree edge or a back edge. First suppose that is first discovered by exploring edge . Then by definition, is a tree edge. Moreover, must have been discovered before because once is explored, is necessarily discovered. Now suppose that isn't first discovered by . Then it must be discovered by for some . If hasn't yet been discovered then if is explored first, it must be a back edge since is an ancestor of . If has been discovered then is an ancestor of , so is a back edge.
22.3-7
Rewrite the procedure , using a stack to eliminate recursion.
See the C++ demo.
Also, see this issue for @i-to's discussion.
DFS-STACK(G)
for each vertex u ∈ G.V
u.color = WHITE
u.π = NIL
time = 0
for each vertex u ∈ G.V
if u.color == WHITE
DFS-VISIT-STACK(G, u)
DFS-VISIT-STACK(G, u)
S = Ø
PUSH(S, u)
time = time + 1 // white vertex u has just been discovered
u.d = time
u.color = GRAY
while !STACK-EMPTY(S)
u = TOP(S)
v = FIRST-WHITE-NEIGHBOR(G, u)
if v == NIL
// u's adjacency list has been fully explored
POP(S)
time = time + 1
u.f = time
u.color = BLACK // blackend u; it is finished
else
// u's adjacency list hasn't been fully explored
v.π = u
time = time + 1
v.d = time
v.color = GRAY
PUSH(S, v)
FIRST-WHITE-NEIGHBOR(G, u)
for each vertex v ∈ G.Adj[u]
if v.color == WHITE
return v
return NIL
22.3-8
Give a counterexample to the conjecture that if a directed graph contains a path from to , and if in a depth-first search of , then is a descendant of in the depth-first forest produced.
Consider a graph with vertices , , and , and with edges , , and . Suppose that first explores , and that 's adjacency list has before . We next discover . The only adjacent vertex is , but is already grey, so finishes. Since is not yet a descendant of and is finished, can never be a descendant of .
22.3-9
Give a counterexample to the conjecture that if a directed graph contains a path from to , then any depth-first search must result in .
Consider the directed graph on the vertices , and having the edges , , then there is a path from to . However, if we start a at and process before , we will have which provides a counterexample to the given conjecture.
22.3-10
Modify the pseudocode for depth-first search so that it prints out every edge in the directed graph , together with its type. Show what modifications, if any, you need to make if is undirected.
If is undirected we don't need to make any modifications.
See the C++ demo.
DFS-VISIT-PRINT(G, u)
time = time + 1
u.d = time
u.color = GRAY
for each vertex v ∈ G.Adj[u]
if v.color == WHITE
print "(u, v) is a tree edge."
v.π = u
DFS-VISIT-PRINT(G, v)
else if v.color == GRAY
print "(u, v) is a back edge."
else if v.d > u.d
print "(u, v) is a forward edge."
else
print "(u, v) is a cross edge."
u.color = BLACK
time = time + 1
u.f = time
22.3-11
Explain how a vertex of a directed graph can end up in a depth-first tree containing only , even though has both incoming and outgoing edges in .
Suppose that we have a directed graph on the vertices and having edges and . Then, has both incoming and outgoing edges.
If we pick our first root to be , that will be in its own tree. Then, we pick our second root to be , since the only thing it points to has already been marked , we won't be exploring it. Then, picking the last root to be , we don't screw up the fact that is along in a tree even though it has both an incoming and outgoing edge in .
22.3-12
Show that we can use a depth-first search of an undirected graph to identify the connected components of , and that the depth-first forest contains as many trees as has connected components. More precisely, show how to modify depth-first search so that it assigns to each vertex an integer label between and , where is the number of connected components of , such that if and only if and are in the same connected component.
The modifications work as follows: each time the if-condition of line 8 is satisfied in , we have a new root of a tree in the forest, so we update its label to be a new value of . In the recursive calls to , we always update a descendant's connected component to agree with its ancestor's.
See the C++ demo.
DFS-CC(G)
for each vertex u ∈ G.V
u.color = WHITE
u.π = NIL
time = 0
cc = 1
for each vertex u ∈ G.V
if u.color == WHITE
u.cc = cc
cc = cc + 1
DFS-VISIT-CC(G, u)
DFS-VISIT-CC(G, u)
time = time + 1
u.d = time
u.color = GRAY
for each vertex v ∈ G.Adj[u]
if v.color == WHITE
v.cc = u.cc
v.π = u
DFS-VISIT-CC(G, v)
u.color = BLACK
time = time + 1
u.f = time
22.3-13
A directed graph is singly connected if implies that contains at most one simple path from to for all vertices . Give an efficient algorithm to determine whether or not a directed graph is singly connected.
This can be done in time . To do this, first perform a topological sort of the vertices. Then, we will contain for each vertex a list of it's ancestors with . We compute these lists for each vertex in the order starting from the earlier ones topologically.
Then, if we ever have a vertex that has the same degree vertex appearing in the lists of two of its immediate parents, we know that the graph is not singly connected. however, if at each step we have that at each step all of the parents have disjoint sets of degree vertices as ancestors, the graph is singly connected. Since, for each vertex, the amount of time required is bounded by the number of vertices times the of the particular vertex, the total runtime is bounded by .