Graphs Assignment Solutions

  1. Bridge Connected Components

    Let G= (V, E) be an undirected graph. An edge e E is a bridge if the removal of e increases the number of connected components in G. If E' is the set of bridges in G then each connected component in G' = (V, E-E') is said to be a bridge connected component of G. Design an algorithm to determine the bridge connected components and the bridges in an undirected graph. Analysize the time complexity of your algorithm.

    If <x,y> = e is a bridge (without loss of generality, suppose x is the parent of y on a DF traversal ( nondirected graph ) then I further claim DFN(y) = L(y) (this is important - it determines cycles).

    For visual verification remember Figure 6.9 and with DFN and lows. Also a new one Conveniently, the DFN and node number are the same here

    With this information, we can use the algorithm in Horowitz which finds

    1. articulation points
    2. biconnected components
    but rather than getting the biconnected components, we shall find the bridge connected components (slight change to Horowitz code will result in loops or singleton vertices).
    Algorithm ART2(u,v)    // same comments as in book's ART(u,v) algorithm
       // including initialization of global DNF to 0, num to 1, PARENT array, etc.
       // initialize the stack to contain (0,1), see  **
    { DFN(u) = num; L(u) = num; num = num + 1
      PARENT(u) = v
      for each vertex w adjacent from u do
        if v != w and DFN(w) < DFN(u) then add (u, w) to top of Stack //endif
        if DFN(w) = 0 then
          {
            call ART2(w,u) // w is unvisited
    	if L(w) > DFN(u) then 
    	    {
    	     print ("new biconnected component")
    	     delete an edge fromm top of Stack.  Let this edge be (x,y)
    	     if (x,y) = (u, w) or (x,y) = (w,u) then  //first edge matches - should anyway
    	        { 
    		 if BFN(y) = L(y) then 
    		   {
    		    print (y)  // singleton
    		    print ("Bridge", "(", PARENT(y), ",", y , ")")
    		    } 
    		  //endif
    		 }
    	      else 
    	         {
    		   print ("(", x, ",", y , ")")   //first in cycle
    		   loop
    		     delete an edge from top of Stack. Let this edge be (x,y)
    		     print ("(", x, ",", y , ")")
    		   until (((x,y)=(u,w) OR (x,y)=(w,u)) AND (BFN(y)=L(y)) )
    		   pop next element of Stack - call it (x,y) //bridge connection for loop
          	  	   print ("Bridge", "(", x, ",", y , ")")
    		 }
    	       // endif (x,y) = (u, w)
                  }
    	 // endif L(w) > DFN(u) 
    	 L(u) = min(L(u),L(w))	                
           }
        else if w != v then L(u) = min (L(u), DFN(w))  //endif
        // endif
       repeat  // for 
    }
    

    Comment: the pop after the loop is to remove bridges between loops from Stack, i.e.,

    has biconnected components

    however, this is only a bridge for bridge connected components. The pop will get it out of the Stack.


    Analysis of time complexity:

    Changes to Horowitz algorithm [ O(v+e) ] were merely a few more if statements to compare.
    the time complexity thus remains the same.
    (Span Tree O(v+e) traverse through and a calculation for each node O(v+e))

    ** I should initialize the stack to contain (0,1) to ensure there is always an edge to pop after loop.
    Also to ensure that will get components 1, 2, and 3

  2. Euler partition

    An Euler partition of a graph G = (V, E) is a partition of E into a set of edge disjoint paths and circuits such that every node of odd degree is at the endpoint of exactly one path. Write an O(|V| + |E|) algorithm to compute the Euler partition.

    Example:

    Graph
    Euler partition

My first solution was the following: Page 1, the code, examples,
but then I did not like the adjacency list use.
Second idea was as follows:

  1. Create a depth first spanning tree (choose a node with > 1 connection) - keeping track of backedges to/from nodes (mark these). Also use a dirty bit to indicate which nodes are of odd degree. (Implementation: Initially set dirty bit (one for each node) to 0. This indicates all nodes are even. Each time a node is referenced when creating the DFS span tree, change the bit. Thus, if referenced 3 times (0 1 0 1) the dirty bit is 1, implying odd degree. (similar to (e o e o))

  2. Begin after node 1 of DFS tree and scan down the nodes until you reach the first node which is odd. Remember this value (Nk), then using only backedges (or off-shoots from the main DFS tree) find a path back to node 1.These backedges must exist if we have odd degree. If, in this scan, you reach a node with no more back-edges (or offshoots) then scan up the tree if possible, but then again use off-shoots or back-edges.

      If the tree is sparse, this takes constant time wrt edges O(E) as there are not many choices.
      If the tree is nearly complete, it also takes constant time wrt edges O(E) since there are so many edges - any path works initially - then as algorithm progresses - choices become sparse
    This step will create a circuit (no endpoint)
    The Nk remembered - is of odd degree - first endpoint of next path.
    As edges (and backedges) are used above, they should be removed from the tree (marked "used") - put as output if desired.

  3. From Nk (set dirty bit to 0 -> used) scan down DFS tree until you reach the next odd node (again, since odd, it must have backedges). Follow the method of (2) (remember this node N'k now) and follow back-edges (or offshoots) until you reach another odd node. (i.e., check the dirty bit. If 0, continue (already used that odd at end), if 1 stop and set to dirty)
    You have your first path. If, in this scan, you reach a node with no more back-edges (or offshoots) then scan up the tree if possible. If not, scan down until the odd node is reached
      For Nk you scan down until you reach the next odd N'k. During this process, and the remainder of the scan, vertices used should be marked - [a simple array(1;n). Check if 0 (ok) change to 1. If 1, upon completion take action required (this is required when a loop occurs within the path (Examples B and C). The loop can simply be removed, then continue path.). When path ends, as you remove the edge from the tree, also reset its corresponding array vertices to 0] (Time O(# vertices of specific path))
    Again, remember that each time an edge (backedge) is used it should be marked (removed from the tree) so we get disjoint sets.

  4. Repeat (3) starting at the odd node Nk where you began to deviate from the main DF tree in (3)
This algorithm works no matter which DFS tree is used for a given graph. The only stipulation is that node 1 has > 1 connection, thus the singleton edge must be a specifically checked for situation in the beginning code (i.e., explicitly stated)

Examples: A, B, C