Lecture 28:  Graphs 

Breadth-first traversal

Use bft to answer the following questions:

·       is the graph connected? – follow preds & successors

·       is there a path from node j to node k?

·       what are all the nodes reachable from j?

·       what is the shortest path from one node to another?

NOT:  does the graph have a cycle?, or  topological ordering

idea:  visit all nodes 1-away from n,

          visit all nodes 2-away from n,

                etc.

·       similar to level-order tree traversal

·       use a Queue

Example:                            

            A              B                                   A

C    E   F

 
                                                             

  B

 
            C              D                              

                                               

            E              F           

·       like dft, must mark each node to avoid visiting more than once  (e.g., node F in the example above)

bft(n)  algorithm -- simulate on the above example

·       mark n visited

·       enqueue(n)

·       while queue not empty

o      dequeue m

§       for each unvisited successor s of m,

mark s, enqueue s

 

Simulate bft(A):

 

            A              B                              

                                                            

            C              D                              

                                               

            E              F                 

 

:

implementation of breadth-first graph traversal:

static void bft(Graphnode n)

1  // unmark all nodes

2  n.mark = true;  // call it “marked”, not “visited”

3  Q.enqueue(n) // into an empty Q

4  while (!Q.empty()) {

5    Graphnode m = (graphnode) Q.dequeue(); 

6    Iterator it = m.getSuccessors().iterator();

7    while(it.hasNext()) {

8      Graphnode s = (Graphnode) it.next();

9      if (!s.mark) {

10       S.mark = true;

11       Q.enqueue(S)

       }

     }

  }

}

Time for Breadth-first Traversal

·       each reachable node is enqueued/dequeued once

·       when a node is dequeued, each of its successors are considered

so, the total time is 

T(n) = c1 + (c2 + e1 ´ c3) + (c2 + e2 ´ c3) ++  (c2 + er ´ c3)

 c1 = time for statements 1, 2, and 3

 c2 = time for statements 4, 5, and 6;

 c3 = time for statements 7, 8, and 9;

 ei = number of successor edges for ith node dequeued

 r = number of nodes reachable from n

In the worst case, r = N:

     T(n) = c1 + N ´ c2 +  c3´ (e1 + e2 ++ eN) 

            = O(N + E)

Summary:

depth-first traversal:  for each path, go as far away as possible

breadth-first traversal:  visit all nodes 1 away, then all nodes 2 away, etc

Example:

        A        ®        B   ¬   C

        ¯                   ¯

        D  ¬   E   ¬   F

                              ¯

                              G

You try:

·       find 2 different orderings for dft(A)

A B F G D E      and    A D F G E B

·       find 2 different orderings for bft(A)

A B D F G E      and    A D B F E G

·       for what node(s) n is there an order that is both dfs(n) and bfs(n)

D:  D F E G     or    E:  E D F G      or   G:  G D F  E

Recall:  code for dft(n) and bft(n):

// unmark all nodes

dft(n) {

  mark n  // call it "mark", not "visit"

  for each unmarked successor m of n {

    dft(m)

  }

}

 

bft(n)

  // unmark all nodes

  mark n

  Q.enqueue(n) (into an empty Q)

  while Q not empty {

    m = Q.dequeue() 

    for each unmarked successor S of m {

      mark S  // call it "mark", not "visit"

      Q.enqueue(S)

    }

  }

}

 

Where should you print the node that is visited?

        dft:  when you mark it

        bft:  when you enqueue it or when you dequeue it

Path detection - is there a path from node j to node k?

1.      start with all nodes unmarked

2.    do dfs(j) or bfs(j), then check wheter k is marked

time = O(N+E)

Note:  extra work is done;

You try:  write the code for a more efficient isPath method

boolean isPath(n,goal) {

  // precondition: all nodes are unmarked

  // fill in your code here

}

Solution based on bfs:

  if (n = goal) return true;

  mark n;

  Q.enqueue(n)

  while (!Q.empty()) {

    m = Q.dequeue();

    for each unmarked successor s of m {

      if (s = goal) return true;

      mark s

      Q.enqueue(s);

    }

  }

  return false;

solution based on dfs:

  if (n == goal) return true;

  mark n

  for each unmarked successor s of n {

    if (isPath(s,goal)) return true;

  }

  return false;

Cycle Detection:  use dfs(n):

Q: when doing a dfs, what will happen to indicate a cycle?

A: a successor will be marked (i.e., already looked at)

but this isn't sufficient to know that there's a cycle

use 3 values:  "unmarked", "in-progress", "done"

Boolean hasCycle(n) {

  mark n "in progress"

  for each successor m of n {

    if m is "unmarked" if (hasCycle(m)) return true;

    if m is "in progress" return true;

  }

  mark n "done"

}

Simulate hasCycle(X) for :     X  ®   A   ®  B  ®  D

                                                         ­        ¯

                                                          ¬ ¬  C

 

Can we identify just the nodes that are in the cycle?

 

Note: 

To find whether a graph contains a cycle, it isn't sufficient to call hasCycle() on just one of the nodes;

instead:  call hasCycle on an unmarked node, and then repeat on another unmarked node

Graphs Summary

·       Graph = set of nodes and set of edges

·       two kinds of graphs:  directed, undirected

·       low-level operations:

o      add a node

o      add an edge

o      remove a node

o      remove an edge

o      is there an edge from node j to node k?

o      what are all the successors of node j?

·       high-level operations:

o      dft:  reachability, cycle detection, topological order

o      bft:  reachability, shortest path

·       there are algorithms for finding the strongly connected components – covered in CS 577 (?)