Reference Link: https://www.byvoid.com/blog/scc-tarjan/
My algorithm library: Https://github.com/linyiqun/lyq-algorithms-lib Algorithm Introduction
As the title describes, the goal of the Tarjan algorithm is to find the diagram of the connected graph, in fact, the precondition is this, in a direction diagram, sometimes there is bound to be a node into the ring, and in the diagram of the formation of the Ring of the nodes formed by the diagram is what we are looking for, this in real life is very useful, can help us solve problems such as the shortest distance. algorithm principle Concept
Before we introduce the principle of the algorithm, we must first understand some concepts:
1, strong connectivity. In the graph G, if there is at least one path between the 2 points, we call these 2 points strong connectivity.
2, strong connectivity diagram. In Figure G, if any of the 2 points are strongly connected, the graph G is a strongly connected graph.
3, strong connectivity components. Not all of the graphs in any of the 2 points exist between the path, some are part of the node connectivity, we call such a graph is a non-strong connected graph, wherein the part of the strong connected sub-graph, is called the strong connected component.
The strong connectivity component is what this algorithm is looking for. An illustration is given below:
In the above diagram, {1, 2, 3, 4} is a strongly connected component, because 5,6 is not up to 1, 2, 3, 4, and here also 5, 6 alone as a strong connected component, can be understood as self to be accessible (so the interpretation feels more reluctant, but the definition also allows such a situation). the process of the algorithm
The algorithm defines 2 variables for each node dfn[i] and low[i],dfn[i], which means the search order number of the I-node, low[i] represents the order number of the earliest nodes that can be traced back to the I node or the child of I, and if that's not understood, it doesn't matter, You can look at the following pseudo-code:
Tarjan (U)
{
Dfn[u]=low[u]=++index// for node U set order number and low initial value
Stack.push (u) //Push node u into stack for each
( U, v) in E //Enumerate each edge if
(v is not visted)//If node V has not been accessed
Tarjan (v) //Continue looking down
low[u] = min (Low[u], LOW[V])
else if (v in S) //If node V is also within the stack
low[u] = min (Low[u], dfn[v]) if
(dfn[u] = = Low[u]) //If node U is the root of the strongly connected component
repeat
v = s.pop //Will V-Stack, a vertex in the strongly connected component
Print v
until (u== v)
}
implementation of the algorithm
The implementation of the algorithm is an example of the above example, the input data graphData.txt:
1 2
1 3
2 4
3 4
3 5
4 1
4 6 5
6
The input format for the label 1 marks 2, which means that the presence of the label 1 points to the side of the label 2 node.
The Graph.java graph class is:
Package Tarjan;
Import java.util.ArrayList;
/** * to Graph class * * @author LYQ * * */public class Graph {//graph contains the label of the point arraylist<integer> vertices;
The graph contains the distribution of the forward edge, Edges[i][j], i,j represents the figure of the label int[][] edges;
Figure Data arraylist<string[]> Graphdatas;
Public Graph (arraylist<string[]> graphdatas) {this.graphdatas = Graphdatas;
vertices = new arraylist<> ();
}/** * Constructs a map with graph data */public void constructgraph () {int v1 = 0;
int v2 = 0;
int verticnum = 0;
For (string[] array:graphdatas) {v1 = Integer.parseint (array[0]);
V2 = Integer.parseint (array[1]);
if (!vertices.contains (v1)) {vertices.add (v1);
} if (!vertices.contains (v2)) {Vertices.add (v2);
}} Verticnum = Vertices.size ();
Apply more than 1 spaces, is the label and subscript consistent edges = new Int[verticnum + 1][verticnum + 1]; To do the initialization of the edge, 1 represents the side for which the direction is not connected (int i = 1; i < Verticnum + 1; i++) {for (int j = 1; J < Verticnum + 1;
{Edges[i][j] =-1; }} for (STring[] array:graphdatas) {v1 = Integer.parseint (array[0]);
V2 = Integer.parseint (array[1]);
EDGES[V1][V2] = 1;
}
}
}
Algorithm Tool class Tarjantool.java:
Package Tarjan;
Import Java.io.BufferedReader;
Import Java.io.File;
Import Java.io.FileReader;
Import java.io.IOException;
Import java.util.ArrayList;
Import Java.util.Stack; /** * Tarjan algorithm-forward graph strong connected component algorithm * * @author LYQ * * */public class Tarjantool {//Current node traversal number public static int Currentseq
= 1;
Figure Construction Data file address private String graphfile;
Node U search order number private int dfn[];
The order number of the oldest node to which u or U's subtree can be traced to private int low[];
The graph graph of the private graph of graphs constructed by the figure data;
Graph traversal node stack private stack<integer> verticstack;
Strong connectivity component results private arraylist<arraylist<integer>> resultgraph;
The label list of the non-traversed points of the graph private arraylist<integer> remainvertices;
Figure the list of edges not traversed private arraylist<int[]> remainedges;
Public Tarjantool (String graphfile) {this.graphfile = Graphfile;
Readdatafile ();
* * */private void Readdatafile () {File File = new file (graphfile)/** * * *
arraylist<string[]> DataArray = new arraylist<string[]> (); try {BufferedReader in = new BufferedReader (new FileReader (file));
String str;
String[] Temparray;
while ((str = in.readline ()) = null) {Temparray = Str.split ("");
Dataarray.add (Temparray);
} in.close ();
} catch (IOException e) {e.getstacktrace ();
}//Construct a graph with graph = new Graph (DataArray) according to the data;
Graph.constructgraph ();
}/** * Initializes 2 scalar arrays */private void Initdfnandlow () {int verticnum = 0;
Verticstack = new stack<> ();
Remainvertices = (arraylist<integer>) graph.vertices.clone ();
Remainedges = new arraylist<> ();
Resultgraph = new arraylist<> ();
for (int i = 0; i < graph.edges.length; i++) {Remainedges.add (graph.edges[i]);
} Verticnum = Graph.vertices.size ();
DFN = new Int[verticnum + 1];
Low = new Int[verticnum + 1];
Initializes an array of operations for (int i = 1; I <= verticnum; i++) {dfn[i] = Integer.max_value;
Low[i] =-1; }}/** * Search for strong connected components */public void searchstrongconnectedgraph () {int label = 0;
int verticnum = Graph.vertices.size ();
Initdfnandlow ();
Set the first dfn[1]=1;
DFN[1] = 1;
Remove first node label = remainvertices.get (0);
Verticstack.add (label);
Remainvertices.remove ((Integer) 1);
while (Remainvertices.size () > 0) {for (int i = 1; I <= verticnum; i++) {if (graph.edges[label][i] = = 1) {
The node connected to this side is also added to the stack verticstack.add (i);
Remainvertices.remove ((Integer) i);
Dfssearch (Verticstack);
}} Low[label] = SEARCHEARLIESTDFN (label);
Re-backtracking to the first point for DFN and low values of the judgment if (low[label] = = Dfn[label]) {popstackgraph (label);
}} PRINTSCG ();
}/** * Depth-first traversal to find the strong connected component * * @param stack * The current stack of nodes stored * @param seqNum * The order number of the current traversal */
private void Dfssearch (stack<integer> Stack) {int currentlabel = Stack.peek ();
Set the search order number and add 1 currentseq++ on the original basis;
Dfn[currentlabel] = Currentseq;
Low[currentlabel] = SEARCHEARLIESTDFN (CurrentLabel);
Int[] edgevertic; Edgevertic = Remainedges.get (CurrentLabel); for (int i = 1; i < edgevertic.length; i++) {if (edgevertic[i] = = 1) {//If this node is included in the remaining optional nodes, this node adds if (remain
Vertices.contains (i)) {stack.add (i);
} else {//does not contain, then skips continue;
}//The point connected to this edge is added to the stack Remainvertices.remove ((Integer) i);
Remainedges.set (CurrentLabel, NULL);
Continue depth-first traversal of dfssearch (stack);
}} if (Low[currentlabel] = = Dfn[currentlabel]) {popstackgraph (CurrentLabel); }}/** * pops The local result from the stack * * @param label * Popup critical label */private void popstackgraph (int label) {//
If 2 values are equal, the node and the point after this node are moved out of the stack int value = 0;
arraylist<integer> SCG = new arraylist<> ();
while (Label! = Verticstack.peek ()) {value = Verticstack.pop ();
Scg.add (0, value);
} scg.add (0, Verticstack.pop ());
Resultgraph.add (SCG); }/** * The earliest order number that the current node may have searched for * * @param label * Current node designator * @return */private int SEARCHEARLIESTDFN (int label)
{ Determines whether this node has a sub-edge Boolean Hassubedge = false;
int MINDFN = Dfn[label];
If the order number you are searching for is already the smallest order number, return if (dfn[label] = = 1) {return Dfn[label];
} int tempdfn = 0;
for (int i = 1; I <= graph.vertices.size (); i++) {if (graph.edges[label][i] = = 1) {Hassubedge = true; If this node is not included in the stack and in the remaining nodes the description has been rolled back and is not allowed to traverse if (!remainvertices.contains (i) &&!verticstack.contains (i)) {con
Tinue;
} TEMPDFN = SEARCHEARLIESTDFN (i);
if (TEMPDFN < MINDFN) {MINDFN = TEMPDFN;
}}}//If there are no child edges, the search order number is itself if (!hassubedge && Dfn[label]! =-1) {MINDFN = Dfn[label];
} return MINDFN;
}/** * Standard search strongly connected component algorithm */public void STANDARDSEARCHSCG () {initdfnandlow ();
Verticstack.add (1);
Remainvertices.remove ((Integer) 1);
Start searching for DFSSEARCHSCG (1) from the first node labeled 1;
The strongly connected component PRINTSCG () in the output result; /** * Depth First search strong connected component * * @param u * Current search node designator */private void DFSSEARCHSCG (int u) {dfn[u] = cur renTseq;
Low[u] = Currentseq;
currentseq++; for (int i = 1; I <graph.edges[u].length; i++) {//Determine if u,i two node is connected if (graph.edges[u][i] = = 1) {//connected case, when I is not
When you visit, add the IF (Remainvertices.contains (i)) {Verticstack.add (i) in the stack;
Remainvertices.remove ((Integer) i);
Recursive search DFSSEARCHSCG (i); Low[u] = (Low[u] < Low[i]?
Low[u]: low[i]); } else if (Verticstack.contains (i)) {//If it has been accessed and has not been low[u] = (Low[u] < Dfn[i]?
Low[u]: dfn[i]); Low[u] = (Low[u] < Low[i]? Low[u]: low[i]);
If all are judged with low, you can also pass the test}}//finally determine if DFN and low are equal if (dfn[u] = = Low[u]) {popstackgraph (U);
}}/** * Outputs a strongly connected component in a forward graph */private void PRINTSCG () {int i = 1;
String resultstr = "";
System.out.println ("All strongly connected component sub-graphs:");
for (arraylist<integer> graph:resultgraph) {resultstr = "";
ResultStr + = "Strong connectivity Component" + i + ": {";
for (Integer v:graph) {resultstr + = (v + ","); } ResultStr = (String) resultstr.subsEquence (0, Resultstr.length ()-2);
ResultStr + = "}";
System.out.println (RESULTSTR);
i++;
}
}
}
Test class Client.java:
Package Tarjan;
/**
* Tarjan Algorithm--forward graph strong connected component algorithm
* @author lyq
* *
/Public
class Client {public
static void main ( String[] args) {
//Graph constructs data file address
String Graphfilepath = "C:\\users\\lyq\\desktop\\icon\\graphdata.txt";
Tarjantool tool = new Tarjantool (graphfilepath);
The following method for the transformation of a little method, but also a bit of a problem
//tool.searchstrongconnectedgraph ();
TOOL.STANDARDSEARCHSCG ();
}
}
The execution steps of the algorithm are shown in the figure (the screenshot taken by the phone is not effective, please do not take offense):
It mainly shows the assignment of DFN and the low array with the order of traversal, as well as the content change of the stack.
The output of the algorithm:
All strongly connected component sub-graphs: Strongly connected components
1:{6} strong connected Components
2:{5}
Strong connected Components 3:{1, 2, 4, 3}
missing points of the algorithm
In this algorithm, I wrote 2 algorithms, Searchstrongconnectgraph is I did not look at the pseudo-code write, and later found that the meaning of a bit distorted, encountered a circular graph when there will be problems, and then immediately read the pseudo-code, immediately the code to streamline a lot, is really a very powerful algorithm , the 2nd is I think that in the following step, the judgment can be merged together, because I found that the results are consistent, can be used to determine the value of the low array.
In the case of a connection, when I has not been accessed, the stack is added
if (remainvertices.contains (i)) {
verticstack.add (i);
Remainvertices.remove ((Integer) i);
Recursive search
DFSSEARCHSCG (i);
Low[u] = (Low[u] < Low[i]? Low[u]: low[i]);
} else if (verticstack.contains (i)) {
//If it has been visited and has not been
low[u] = (Low[u] < Dfn[i]? Low[u]: dfn[i]);
Low[u] = (Low[u] < Low[i]? Low[u]: low[i]); If you use low to judge, you can also pass the test
}
It may be that the edges in the diagram are allowed to be traversed only once.
The breakthrough point of the algorithm
It is obvious that the breakthrough of the algorithm is to compare the values of low and DFN, because the value of DFN in the traversal order has been determined, so the key to the problem is the low value of the determination, because the title is to find the first search number, here will use depth first way to find a layer, if found small, is replaced, if the last found is his own time, it means that the middle is actually a ring. The node is then removed from the top of the current node in the stack.