CMPSCI 311 Discussion #2: Depth-First Search

David Mix Barrington

18/20 September 2006

In lecture Monday we presented the greadth-first and depth-first search algorithms. We can describe them in pseudo-code as follows:


    void BFS (vertex v) {
        layer[0] = {v}; i = 0;
        while (layer[i] is nonempty) {
            for (each vertex u in layer[i])
                for (each neighbor w of u)
                    if (w is not yet in any layer)
                        add w to layer[i+1];
            i++;}}

    void DFS (vertex v) {
        mark v as explored;
        for (each neighbor u of v)
            if (u is not yet explored)
                DFS(v);}
  1. Carry out a BFS and a DFS of the following graph, starting at vertex a and visiting neighbors in the order given by this adjacency list. Draw the BFS and DFS trees, using solid lines for tree edges and dotted lines for non-tree edges:

    The BFS finds that level 0 is {a}, level 1 is {b,d,h}, level 2 is {c,e,g}, and level 3 is {f}. The tree looks like this:

            a
          / | \
        b   d   h
        | \   \
        c   g   e
          \ 
            f
    
    with the addition of dotted lines from d to c, h to g, h to e, g to f, and e to f. This graph is also known as the three-dimensional hypercube

    The DFS tree has all eight vertices in a single line, as it happens in alphabetical order, so that h occurs at level 7. We have additional dotted lines from a to d, a to h, b to g, c to f, and e to h. (n≥n0→f(n)≤cg(n).

  2. Recall the recursive definition of reachability in an undirected graph: that u is reachable from v if either u = v or there exists a vertex w such that w is reachable from v and there is an edge from w to u. Prove that if u is reachable from v, then u is marked "explored" after DFS(v) is run.

    We first need to convert the definition into an induction scheme that can prove a property P(u) for all vertices u such that u is reachable from v. I gave such a scheme on the board in lecture:

    In this instance, we can take the predicate P(x) to be "x is marked explored after DFS(v) has run". Then the base case is true because the code of DFS(v) marks v as explored in line 2. For the inductive case, we let u and w be arbitrary nodes and assume that w is marked during DFS(v) and that u is a neighbor of w (this last is the same statement as "E(w,u)"). From the code, we see that w can be marked explored only by DFS running on w, so we may conclude that DFS(w) was run before or during the run of DFS(v). When DFS(w) was run, u was one of the neighbors considered at line 3. If u was not found to be explored in line 4, it was marked explored at line 5. Since there is no way for it to become unmarked, it must have been marked at the end of DFS(v). Since u and w were arbitrary, we have proved the induction step. Since we have completed the induction, we know that P(u) is true for all u that are reachable from v, as desired.

  3. Prove that if t is marked during the execution of DFS(s), then t is a descendent of s in the DFS tree. (Recall the the definition of "descendent" is recursive (and was given incorrectly on the handout in discussion) -- to be a descendent of s a node is either s itself of a child of a descendent of s.)

    The induction needed here is a bit more complicated -- I apologize for not getting it straight and writing it down before the discussion. We can make a recursive definition of the predicate M(x,y) meaning "x is marked during DFS(y)" from the code for DFS(y):

    We want to prove that for any t and s, M(t,s) implies that t is a descendent of s. The problem is that in the inductive case of the definition, the second term of M(x,y) changes, which forces us to use induction on the s rather than on t. Here is an induction scheme that proves P(x) for all x such that DFS(x) finishes:
    • Prove P(x) for any x with no unexplored neighbors.
    • Prove that if P(y) is true for every unexplored neighbor y of x, then P(x) is true.

    Actually the "base case" here is just a special case of the inductive case, because if you have no unexplored neighbors then by definition any predicate must be true for all of your unexplored neighbors.

    So let's prove this with P(x) being the statement "for all y, if y is marked during DFS(x), then y is a descendent of x".

    For the base case, assume that x has no unexplored neighbors. Then DFS(x) will make no recursive calls, and thus will mark x itself and nothing else. Our base case is true, because the (corrected) definition of "descendent" says that x is a descendent of x.

    Now for the inductive case. We assume that P(y) holds for every unexplored neighbor y of x This means that for every such y, every node marked during DFS(y) is a descendent of y. Now we look at all the nodes marked during DFS(x). These consist of x itself, plus any nodes marked during DFS(y) for one of these y's. The definition of "descendent" says that x is a descendent of x, and that any descendent of any of these y's is a descendent of x (since each such y will be a child of x in the tree). (Technical point: By "unexplored neighbor" we mean "neighbor that is unexplored at the time that DFS(x) looks at it".) So all these nodes are descendents of x, and we have proved P(x). This completes the induction.

Last modified 21 September 2006