Review:: Graphs and Some Graph Algorithms
Review:: Graphs and Some Graph Algorithms
(a) Directed Graphs Definition: G = ( V, E ) is a directed graph with a finite set of vertices (nodes) V, and a finite set of edges E V V ( = { (v1, v2) / v1, v2 V, v1 is a source and v2 is a target of the edge (v1, v2)} ). (b) Path in a Graph Definition: Any sequence of vertices v1, v2,, vn from the set of vertices V such that for each i (vi vi+1)E is called a path in the graph G=(V,E) (with the given set of vertices V and the given set of edges E). Here n is the length of the path (the number of vertices in it). Cycle in a Graph Definition: Any path that begins and ends in a same vertex is a cycle. (d) Root in a Graph Definition: Any vertex that is not a target in any of the graph edges is a root. (e) Cyclic and Acyclic Graph Definition : Graph is cyclic if it contains a cycle and acyclic if it doesnt. Acyclic graph is also called a Forest. (f) Subgraph Definition : Graph G1 = (V1, E1) is subgraph of the graph G = (V, E) iff V1V && E1 V1 V1E. (g) Connected Component of a Graph / Connected Graph Definition : C is a connected component in a graph G iff C is a subgraph of G such that there is a path between any two nodes in C. Graph is connected iff it is its own connected component. (h) Transitive Closure of a Graph Definition : Graph G1 = (V, E1) is a transitive closure of the graph G = (V, E) iff i,jV (there is a path between i and j in G (i,j)E1). Example : C C is a transitive closure of A B A B
(i) Spanning Tree in a Undirected Connected Graph Definition : T = (V, E1) is a spanning tree of graph G = (V, E) iff T is a acyclic connected
subgraph of G (E1 E). Lemma 1 : If G=(V,E) is a directed graph with no roots, then G must contain a cycle. (Note: If theres a root in a graph, there still may be a cycle in it) Lemma 2 : If G=(V,E) is a directed graph with no roots, then for all k<=size(V), either there is an acyclic path in G of length k, or there is a cycle of length less than or equal to k. Implementation for Graphs: boolean[][] Graph; Adjacency Matrix Representation (AMR) Graph[i][j] = true iff theres an edge from i-th to j-th vertex. List[] Graph; Index Array List 328 i means that (i,3), (i,2), (i,8) are edges in the graph. Uses one bit per a pair of vertices. Space Complexity : O(n2), where n is number of vertices in the graph.
Obviously, ALR is preferred to AMR when the number of edges is significantly smaller than the squared number of vertices. Also, if the number of edges is significantly greater than the squared number of vertices, AMR is a preferred implementation for graphs.
for each vV if (dfn[v] == 0) depthFirstSearch(v); .. .. void depthFirstSearch(Vertex v) { dfn[v] = k++; for each wV such that (v,w) is an edge if (dfn[w] == 0) depthFirstSearch(w); } Example : B4 C2 E3
A1 (b) Breadth-First Search Pseudo Code : q.onque(root); while (! q.empty()) { Element e = q.deque(); q.onque(e.children()); mark(e); } Example :
D5
A1 B3 C7 D8 E6 G2 F5 I9 H4 J10
Queue Contents : (1) A (2) GBH (3) BHFE (4) HFECD (5) FECDIJ (6) .
write(v);
} Example : B C E
A D The pseudo code above will write nodes in the following order E, C, A, B, D. Thus, D, B, A, C, E is a topological sort in this graph. Assumption is that the graph is acyclic (why?!?). If theres a path from node i to node j in the graph, the algorithm will cause j to be written out before i. (a) If depthFirstSearch(i) is called before depthFirstSearch(j), depthFirstSearch(i) will not finish until depthFirstSearch(j) terminates, therefore writing j before i gets to be written. (b) If depthFirstSearch(j) is called before depthFirstSearch(i), and theres no path from j to i because the graph is acyclic, depthFirstSearch(j), will get to complete before depthFirstSearch(i) gets called, therefore writing j before i gets to be written. This sort has some surprising properties like in the following example : AB CD in which apart from the obvious topological sort (A,C,B,D), (A,B,C,D) and (C,D,A,B) are also topological sorts (obviously favoring some late (B and D) people over some early ones (C and A, respectively).
dfn[v] = 0;
inProg[v] = false;
} // Do the search for each vV if (dfn[v] == 0) { dFS(v); } .. .. void dFS(Vertex v) { dfn[v] = k++;
inProg[v] = true;
for each wV such that (v,w) is an edge if (inProg[w])
declareACycle();
else if (dfn[w] == 0) dFS (w);
inProg[v] = false;
} Example 1 (no cycle should be declared) : C B
Idea of the algorithm : At any time during the search from a node v, all the nodes for which inProg is true, form a path. Thus, if the next node w has inProg true, there already must have been a path from v to w, and therefore a cycle is formed.
k++;
} .. .. void depthFirstSearch(Vertex v) { // ALL NODES IN THE SAME CONNECTED // COMPONENT WILL HAVE THE SAME K
dfn[v] = k;
for each wV such that (v,w) is an edge if (dfn[w] == 0) depthFirstSearch(w); } Example :
O2 A1 Q1 N1 M1 K4 L4 I4 H4 J4 D2 B2
P2 C3 E3 F3 G3
Costs : (a)
Set_of_Edges prim(Graph g) { Set_of_Vertices V; // Set of all vertices in g Set_of_Vertices U; Vertex x, y; Set_of_Edges T = ; U = {A}; // Can start anywhere while (U != V) { Let (x,y) be the lowest cost edge such that x U and y V-U; T = (x,y); U = {y}; } return T; } (b) Kruskals Algorithm (sketch)
Set_of_Edges kruskal(Graph g) { Set_of_Edges T = ; while (there is more than one set) { Let (x,y) be the minimum cost edge connecting two different sets; T = (x,y); Merge the two sets that x and y belong to; } return T; }
int i, j, k; for (i=0; i<A.length; i++) for (j=0; j<A.length; j++) C[i,j] = A[i,j]; for (k=0; k<A.length; k++) for (i=0; i<A.length; i++) for (j=0; j<A.length; j++) C[i,j] = (C[i,j] || (C[i,k] && C[k,j])); return C; } Invariant of the algorithm : If there is a path from i to j with intermediate nodes with indeces smaller than or equal to k-1, C[i,j] = true (already).
for each vV D[v] = C[A,v]; // C[A,v]=+ for nodes with no edge from A to them for (int i=1; i<|V|(=n); i++) { // Move the next element from V-S into S choose w from V-S such that D[w] is minimum; add w to S; // Adjust the shortest distances for remaining elements in V-S for each vV-S D[v] = min( D[v], D[w] + C[w,v] ); } The algorithm will maintain three sets during its execution : S V-S D set of vertices from the graph for which the shortest path from the starting node to them has already been calculated. set of vertices from the graph for which the shortest path from the starting node to them has NOT been calculated yet. set of distances of shortest paths for the vertices from S.
Initially : (0) S = {A}, V-S={B,C,D,E}, D={sp(A,A)=0, sp(A,B)=3, sp(A,E)=5, sp(A,C)=sp(A,D)=+ }. (1) (2) (3) (4) S = {A,B}, V-S={C,D,E}, D={sp(A,A)=0, sp(A,B)=3, sp(A,E)=5, sp(A,C)=9, sp(A,D)=7}. S = {A,B,E}, V-S={C,D}, D={sp(A,A)=0, sp(A,B)=3, sp(A,E)=5, sp(A,C)=9, sp(A,D)=6}. S = {A,B,E,D}, V-S={C}, D={sp(A,A)=0, sp(A,B)=3, sp(A,E)=5, sp(A,D)=6, sp(A,C)=7}. S = {A,B,E,D,C}, V-S=, D={sp(A,A)=0, sp(A,B)=3, sp(A,E)=5, sp(A,D)=6, sp(A,C)=7}.
Invariants of the algorithm: (a) wS D[w] is the cost of the cheapest path from A to w. (b) wV-S D[w] is the cost of the cheapest path from A to w whose vertices (other than w) are all in S. xS yV-S D[x]<=D[y].