Kosaraju algorithm analysis: Solving graph strongly connected components, kosaraju Algorithm
You are welcome to discuss it. please correct me if any errors exist.
If you need to reprint, please indicate the source http://www.cnblogs.com/nullzx/
1. Definition
Connected Component: In an undirected graph, it is a connected subgraph.
Contains a total of four connected components. Vertex A, B, C, and D constitute A connected component. vertex E forms A connected component. vertex F, G, and H constitute two connected components respectively.
Strongly Connected Component: In a directed graph, in a subgraph composed of as many vertices as possible, these vertices are mutually accessible, and these vertices become a strongly connected component.
There are three strongly connected components, namely a, B, e, f, g, c, d, and h.
2. Method for Solving connected components
For the connected component of an undirected graph, a DFS is performed starting from any vertex of the connected component, and all vertices of the connected component can be traversed. Therefore, the number of connected components of the entire graph is equivalent to traversing the entire graph several times (the outermost layer) DFS. All vertices traversed in a DFS belong to the same connected component.
The following describes how to solve the strongly connected component of a directed graph.
3. Basic principles of Kosaraju Algorithm
Let's use a simple example to explain the Kosaraju algorithm.
Obviously there are two strongly connected components, that is, strongly connected component A and strongly connected component B, which are composed of vertex A0-A1-A2 and vertex B3-B4-B5 respectively. Each connected component has several mutually accessible vertices (three in this example). The strongly connected component and the strongly connected component do not form a ring. Otherwise, these connected components should be considered as a whole, the same strongly connected component.
Now let's imagine if we can solve the strongly connected component of a directed graph based on the idea of finding connected components in an undirected graph. We assume that DFS starts from any vertex of Strongly Connected Component B, so it takes two DFS times to traverse the entire graph, which is equal to the number of connected components, in addition, each DFS traversal vertex exactly belongs to the same connected component. However, if we start DFS from any vertex in component A, we cannot get the correct result, because at this time we only need to access all vertices once. Therefore, we should not follow the natural order of vertex numbers (0, 1, 2 ,......) Or perform DFS in any other order. Instead, the DFS should be performed in the previous order based on the vertices of the strongly connected component. From Strongly Connected Component A to strongly connected component B. Therefore, we follow
B3, B4, B5, A0, A1, A2
To achieve our goal. But in fact, this order is too strict. We only need to ensure that at least one vertex of the strongly connected component is placed before all vertices pointing to this connected component. For example
B3, A0, A1, A2, B4, B5
B3 is placed before all vertices of strongly connected component.
Now, the key question is how to obtain such a vertex order that meets the requirements. Kosaraju provides the solution: reverse image retrieval, then, we start to traverse the reverse descending order of DFS from any node of the undirected graph. The obtained order of the reverse descending order must meet our requirements.
DFS's reverse post-order traversal means that if the current vertex is not accessed, it first traverses all other vertices connected to the current vertex but not accessed, and then adds the current vertex to the stack, the order from the top of the stack to the bottom of the stack is the vertex order we need.
Indicates the reverse direction of the source image.
Let's assume that DFS starts from any node in strongly connected component. After the first DFS is completed, the stack is all the vertices of strongly connected component A. After the second DFS is completed, the top of the stack is the vertex of Strongly Connected Component B. This ensures that all vertices of Strongly Connected Component B from the top of the stack to the bottom of the stack are before the vertex of strongly connected component.
Now let's assume that DFS starts from any vertex in Strongly Connected Component B. Obviously, we only need to perform a DFS job to traverse the entire graph. Because it is a reverse traversal, the starting vertex must be completed at the end. Therefore, the vertex at the top of the stack must be the vertex in Strongly Connected Component B, this is obviously the result of vertex sorting we want.
The simplest example is used to illustrate the principle of the Kosaraju algorithm. It is still applicable to scenarios where multiple strongly connected components and complex connections exist. You can perform the verification by yourself.
In summary, no matter which vertex you start with, the number of strongly connected components in the graph ensures the order of vertices in the stack to be traversed: at least one vertex of the strongly connected component is placed before all vertices that point to the connected component. Therefore, the steps for solving strongly connected components can be divided into two steps:
(1) obtain the inverse of the source image, starting from any vertex, and then performing reverse DFS traversal on the undirected graph
(2) perform DFS traversal on the source image based on the order of the vertices in the stack in reverse traversal. All vertices accessed in a DFS traversal belong to the same strongly connected component.
4. Code implementation for solving connected components and strongly connected components
Test Data
| 10 15 0 1 0 4 1 0 1 8 2 1 2 4 2 7 3 4 4 3 5 0 5 6 7 9 7 4 8 5 9 2 |
|
Running result
| Graph Representation 0: 1 4 1: 0 8 2: 1 4 7 3: 4 4: 3 5: 0 6 6: 7: 9 4 8: 5 9: 2 Number of connected components: 4 And vertex 0 belong to the vertex of the same connected component. 0 1 5 8 And vertex 3 belong to the vertex of the same connected component. 3 4 And vertex 9 belong to the vertex of the same connected component. 2 7 9 And vertex 6 belong to the vertex of the same connected component. 6 |
ConnectedComponents includes undirected graph for connected components and Kosaraju Algorithm Implementation
Package datastruct; import java. io. file; import java. io. fileNotFoundException; import java. io. fileReader; import java. util. using list; import java. util. list; import datastruct. graph. edge; public class ConnectedComponents {private boolean [] marked;/* used to mark which (strong) connected component of each vertex belongs to the same (strong) connected component vertex of the same (strong) connected component) the number of connected components is the same */private int [] id; private int count; // (strong) connected component number, which also indicates the number of (strong) connected components private Graph g; public ConnectedComponents (Graph g) {This. g = g; marked = new boolean [g. V ()]; id = new int [g. V ()]; if (g. isDirect () {// Method for Determining the strongly connected component of a directed graph // inverse post-Order of the reverse graph DFS, starting from vertex 0, you can start the vertex list <Integer> stack = g. reverse (). reversePostOrder (0); marked = new boolean [g. V ()]; while (! Stack. isEmpty () {int v = stack. pop (); if (! Marked [v]) {dfs (v); count ++ ;}} else {// connected component of an undirected graph for (int I = 0; I <g. V (); I ++) {if (! Marked [I]) {dfs (I); count ++ ;}}} private void dfs (int v) {if (! Marked [v]) {marked [v] = true; id [v] = count; for (Edge e: g. adjEdge (v) {int w = e. other (v); dfs (w) ;}} public int count () {return count ;} // public List of all vertices that belong to the same connected component as vertex v <Integer> allConnected (int v) {vertex list <Integer> List = new vertex list <Integer> (); int k = id [v]; for (int I = 0; I <g. V (); I ++) {if (id [I] = k) {list. add (I) ;}} return list;} public static void main (String [] args) throws FileNotFoundException {File path = new File (System. getProperty ("user. dir ")). getParentFile (); File f = new File (path, "algs4-data/tinyDG2.txt"); FileReader fr = new FileReader (f); Graph graph Graph = new Graph (fr, true, false); System. out. println ("Graph Representation"); System. out. println (graph); ConnectedComponents cc = new ConnectedComponents (graph); System. out. println ("connected component count:" + cc. count (); System. out. println ("\ n"); System. out. println ("and vertex 0 belong to the same connected component vertex"); for (int I: cc. allConnected (0) {System. out. printf ("%-3d", I);} System. out. println ("\ n"); System. out. println ("and vertex 3 belong to the same connected component vertex"); for (int I: cc. allConnected (3) {System. out. printf ("%-3d", I);} System. out. println ("\ n"); System. out. println ("and vertex 9 belong to the same connected component vertex"); for (int I: cc. allConnected (9) {System. out. printf ("%-3d", I);} System. out. println ("\ n"); System. out. println ("and vertex 6 belong to the same connected component vertex"); for (int I: cc. allConnected (6) {System. out. printf ("%-3d", I);} System. out. println ();}}
The adjacent representation of an image contains many practical methods. However, we mainly use the source image to construct its inverse direction and backward order.
Package datastruct; import java. io. bufferedReader; import java. io. file; import java. io. fileNotFoundException; import java. io. fileReader; import java. io. printWriter; import java. io. reader; import java. io. stringWriter; import java. util. arrayList; import java. util. using list; import java. util. list; import java. util. vertex; public class Graph {private int v; // Number of vertices private int e; // Number of edges private boolean isWeight; // Private boolean isDirect; // whether it is a directed graph private boolean hasCycle; // If the graph contains a ring private Sequence List <Edge> [] adj; // link table // The public static class Edge implements Comparable <Edge> {private final int from; // The start vertex of the Edge private final int; // Edge termination vertex private final double w; // weight public Edge (int from, int to, double w) {this. from = from; this. to = to; this. w = w ;}// returns any vertex public int either () {return from ;}// returns another vertex public int Other (int v) {return v = this. from? To: from;} // used for Directed Graph public int from () {return from;} // used for Directed Graph public int to () {return to;} public double weight () {return w;} // Edge comparator. The weight is based on @ Overridepublic int compareTo (Edge that) {if (this. w> that. w) {return 1;} elseif (this. w <that. w) {return-1 ;}else {return 0 ;}// side display method @ Overridepublic String toString () {return new String (String. format ("%-2d", from) + "" + String. format ("%-2d", to) + "" + String. format ("%-4. 2f ", w) ;}}// public static class Cmp implements Comparator <Edge >{// @ Override // public int compare (Edge e1, Edge e2) {// return e1.compareTo (e2); //} // read the txt file of the graph from the file stream to construct the graph @ SuppressWarnings ("unchecked ") public Graph (Reader r, boolean isDirect, boolean isWeight) {BufferedReader br = new BufferedReader (r); buffers scn = new buffers (br); v = scn. nextInt (); e = scn. nextInt (); this. isWeight = isWeight; this. isDi Rect = isDirect; adj = (partition list <Edge> []) new partition list [v]; for (int I = 0; I <v; I ++) {adj [I] = new vertex list <Edge> () ;}for (int I = 0; I <e; I ++) {int from = scn. nextInt (); int to = scn. nextInt (); double w; if (isWeight) {w = scn. nextDouble ();} else {// if it is not a graph with weight, the default weight is 1 w = 1;} Edge e = new Edge (from, to, w ); adj [from]. add (e); if (! IsDirect) {adj [to]. add (e) ;}} scn. close ();} // The Reverse Graph constructor of the current Graph @ SuppressWarnings ("unchecked") private Graph (Graph g) {v = g. V (); e = g. E (); this. isWeight = g. isWeight; this. isDirect = g. isDirect; hasCycle = g. hasCycle; adj = (partition list <Edge> []) new partition list [v]; for (int I = 0; I <v; I ++) {adj [I] = new vertex list <Edge> () ;}for (int from = 0; from <v; from ++) {for (Edge e: g. adj [from]) {int to = e. other (from); double w = E. weight (); adj [to]. add (new Edge (to, from, w) ;}}// returns the reverse Graph public Graph reverse () {if (this. isDirect) {return new Graph (this);} else {throw new IllegalArgumentException ("Graph is not Directed ");}} // construct the Graph by adding edges @ SuppressWarnings ("unchecked") public Graph (int v, boolean isDirect, boolean isWeight) {adj = (Edge list <Edge> []) new vertex list [v]; for (int I = 0; I <v; I ++) {adj [I] = new vertex list <Edge> ();} This. isDirect = isDirect; this. isWeight = isWeight; this. v = v;} // Add an Edge public void addEdge (Edge e) {adj [e. from]. add (e); this. e ++; if (! IsDirect) {this. e ++; adj [e. to ()]. add (e) ;}/// returns the number of vertices in the graph. public int V () {return v ;}// returns the number of edges in the graph. public int E () {return e ;} // returns the public List of all vertex numbers adjacent to vertex v <Integer> adjVertex (int v) {ArrayList <Integer> list = new ArrayList <Integer> (adj [v]. size (); for (Edge e: adj [v]) {list. add (e. other (v);} return list;} // returns the edge adjacent to vertex v. For classes in the same package, this method is more efficient. public List <Edge> adjEdge (int v) {return adj [v];} // returns an Edge public Edge getEdge (int From, int to) {for (Edge e: adj [from]) {if (e. other (from) = to) {return e;} return null;} // whether it is a directed graph public boolean isDirect () {return isDirect ;} // whether it is a weighted image public boolean isWeight () {return isWeight;} // whether it is a directed acyclic graph public boolean isDAG () {if (! IsDirect) {return false;} boolean [] marked = new boolean [v]; boolean [] onStack = new boolean [v]; for (int I = 0; I <v; I ++) {if (! Marked [I]) {dfs (I, marked, onStack) ;}} return! HasCycle;} // used to determine the DAG depth first traversal private void dfs (int v, boolean [] marked, boolean [] onStack) {if (hasCycle) {return ;} marked [v] = true; onStack [v] = true; for (Edge e: adj [v]) {int w = e. other (v); if (! Marked [w]) {dfs (w, marked, onStack) ;}elseif (onStack [w]) {hasCycle = true; return ;}} onStack [v] = false ;} // display method of the graph public String toString () {StringWriter sw = new StringWriter (5 * v + 10 * e); // The length is not an accurate value, it is recommended that the PrintWriter pw = new PrintWriter (sw); for (int I = 0; I <v; I ++) {pw. printf ("%-3d:", I); for (Edge e: adj [I]) {if (isWeight) {pw. printf ("[%-3d, %-4.2f]", e. other (I), e. w);} else {pw. printf ("%-3d", e. other (I );} Pw. println ();} return sw. getBuffer (). toString ();} // whether the edge from to exists // public boolean hasEdge (int from, int) {// boolean [] marked = new boolean [v]; // hasEdge0 (from, to, marked); // return marked [to]; ///} // private void hasEdge0 (int from, int to, boolean [] marked) {// if (! Marked [from]) {// marked [from] = true; // for (Edge e: adj [from]) {// if (! Marked [to]) {// hasEdge0 (e. other (from), to, marked); //} else {// return; //}/// // start from the from node and start reverse and backward traversal. The stack public Sequence List <Integer> reversePostOrder (int from) in reverse and backward order is returned) {shortlist <Integer> stack = new shortlist <Integer> (); boolean [] marked = new boolean [v]; for (int I = 0; I <v; I ++) {reversePostOrderTar (I, stack, marked) ;}return stack ;}// for depth-first traversal of private void reversePostOrderTar (int from, sorted list <Integer> Stack, boolean [] marked) {if (! Marked [from]) {marked [from] = true; for (Edge e: adj [from]) {reversePostOrderTar (e. other (from), stack, marked);} stack. push (from) ;}} public static void main (String [] args) throws FileNotFoundException {File path = new File (System. getProperty ("user. dir ")). getParentFile (); File f = new File (path, "algs4-data/tinyDG.txt"); FileReader fr = new FileReader (f); Graph g = new Graph (fr, true, false); System. out. println (g. toString (); System. out. println (g. reverse (). toString (); // System. out. println (g. hasEdge (0, 7 ));}}5. References
[1]. algorithm (4th) Robert Sedgewick people's post and telecommunications Press