Preface: I have integrated and referred to some related articles on the Internet and some of the internal content in Liu rufu Jia's "internal power law.
The basic process of strong-connected components, blocks, cut points, and bridges is DFS, so we will introduce some basic knowledge about DFS.
For the sake of convenience, it is stipulated that the DFS process will color the nodes: White is the point that has not been considered, black is the point that has been fully considered, and gray is the point that has been found but not processed. The gray points constitute the traversal boundary.
DFS Traversal
DFS traversal preferentially expands the newly discovered node. Its process can be seen as recursion. The algorithm will get the DFS forest, similar to the BFS tree. DFS supports edge classification. First, we need to add the time stamp (Time Stamp) to the time duration ).
The time when the d [u] node is grayed out
End Time f [u] the time when the node turns black
The initialization time is 0, all vertices are white, and the DFS forest is empty. You only need to execute a DFS-VISIT (u) for each White Point U, each time processing a point before and after processing the current time plus 1, the timestamp of all vertices u must be 1 <= d [u] & F [u] <= 2 | v |. Obviously, the time complexity is O (n + M.
Here is an example:
Here is an example:
All timestamps are different and are integers from 1 to 2 | v |. We can also intuitively see that for any node pair (u, v), range [d [u], F [u] and [d [v], f [v] is either completely isolated or mutually contained. If the range of U is completely included in the range of V, U is the descendant of V in the DFS tree.
We have important information about the DFS tree:
White path TheoremIn the DFS forest, V is the descendant of U. When and only when u is just discovered, V can depart from U and only arrive at the White node. The theorem is very intuitive and the proof is omitted.
DFS-VISIT can divide edges (u, v) into the following categories by the way:
Tree edges (t) V are detected by edges (u, v.
Back edges (B) U is the descendant of v.
Forward edges (f) V is the descendant of U.
Cross edges (c): Other edges. You can connect two nodes in the same DFS tree without any descendant relationship, or the nodes in different DFS trees.
Edge Classification AlgorithmTo apply the classification rules to the program, you only need to check the color of V when considering the side (u, v:
V is white, (u, v) is t side
V is gray, (u, v) is B side, so only at this time u ancestor is gray
V is black. Continue to judge. If d [u] <D [v], it indicates that V is the descendant of U, so it is the F side, otherwise it is the c side. Otherwise, it is the c side.
The time complexity of DFS is still O (N + M ). It is easy to prove that undirected graphs only have T and B sides. The figure below makes the DFS tree in the previous example more intuitive. You can carefully understand what the T, B, F, and C sides are like.
Directed Graph connectivity
The edges of a directed graph are unidirectional, so the accessibility is not passed: u can reach V, but not V to u. However, if u and v are mutually reachable, the accessibility between w and U is the same for any other node W. The "mutual reachable" relationship is an equivalent relationship. Therefore, all nodes can be divided into several sets based on this relationship. The points in the same set can be mutually reachable, and the points in different sets cannot be mutually reachable, for example:
Each set is called a directed graph.Strongly Connected Component(Strong connected component, SCC ). If a set is regarded as a vertex, all SCC constitute an SCC diagram:
Tarjan Algorithm for strongly connected components
For each strongly connected component scc c, there is an oldest point found in the DFS process. If it is set to X, the path theorem is used, other vertices in C are descendants of X. If we can output C immediately when x access is complete. In this way, all SCC can be distinguished in a DFS tree. Therefore, the key to the problem is to determine whether a vertex is the first detected vertex in SCC.
For example, a solid line represents an edge, and a dotted line represents one or more edges. Suppose we are determining whether U is the first detected node of an SCC. If we can reach U's ancestor W from u's son, it is clear that U, V, and W are in the same SCC, so u is not the first detected point in the SCC. If you can only reach U at most from V, U is the first detected node of the SCC. In this way, the problem is transformed to the result of the D value of the ancestor that can be reached at the far distance of a vertex u. Note: Here, "Arrival" can pass through side B and cannot pass through side C, but the premise is that only some points in the subtree can be searched through the current one (saved by stack in the program) instead of other points in the existing SCC.
Define low [u] As the timestamp dfn [v] (dfn [v]) of the earliest (first discovered) ancestor V that can be traced back to U and its descendants, which indicates the time when V is grayed out, that is, the time when V was first found)
The program uses the sta stack to save the nodes in the current SCC (note that these nodes form a subtree, not necessarily a chain ). Idx indicates the timestamp, SCNT indicates the SCC counter, Id [I] indicates the SCC number where I is located, and insta [I] indicates whether I is in the stack. Note that the limit V must be in the stack, because it may arrive at the output SCC starting from the C side.
Vector <int> adj [N]; int low [N], dfn [N], Id [N], Sta [N], idx, top, SCNT; // SCNT starting from 1 bool insta [N]; void Tarjan (int u) {insta [u] = 1; STA [top ++] = u; low [u] = dfn [u] = ++ idx; For (INT I = 0; I <(INT) adj [u]. size (); I ++) {int v = adj [u] [I]; // If (! Dfn [v]) {Tarjan (V); low [u] = min (low [u], low [v]);} else if (insta [v] & dfn [v] <low [u]) low [u] = dfn [v];} if (low [u] = dfn [u]) {int TMP; ++ SCNT; do {TMP = sta [-- top]; // vis [TMP] = 0; an error is returned. Insta [TMP] = 0; // The tag in the stack should be removed. Id [TMP] = SCNT;} while (TMP! = U );}}
Undirected graph connectivity
It is common to evaluate the connected components of an undirected graph, but the connectivity problem of an undirected graph is far more than that. If a vertex u exists in the connected undirected graph G, after U is deleted, it is called an articulation point of G ). A connected graph without a cut-down is called a biconnectedgraph ). For any two edges E1 and E2, if e1 = e2 or they are in the same ring, it is said that they satisfy the relationship R. It is easy to prove that R is an equivalent relationship. Based on this equivalence relationship, we divide the edge of G into non-intersection E1, E2 ,..., Ek. If VI is a set of points contained in EI, each subgraph gi = {vi, Ei} is called a dual-connected component of g (biconnected
Component, bcc) or block ). The connected component has the following properties:
1. The dual-connected components are both dual-connected.
2. Any two different dual-connected components have at most one public point.
3. node u is the top cut of graph G when it is only a public point of two different double-connected components.
Note that in an undirected graph, there are only two types of B and T sides. Therefore, DFS can easily find all the dual-connected components of G. First, we need to determine all the cutovers. We have the following theorem about top pruning.
Theorem:For a DFS tree that connects undirected graph G = {v, e}, S = {v, t} to G, then, the node u is the top cut of G if and only if one of the following conditions is met:
1. U is the root of T and U has at least two sons
2. U is not the root of T and A son W of U exists, so that the descendant of W or W does not connect back to the ancestor of U (note, not connect back to the U itself ).
Proof:First, consider that U is the root. If it has two sons V and W, V is first discovered. Because W is not the descendant of V, there is no white path between V and W. Because V and W are connected, and only U is marked as gray at this time, all vertices from V to W must pass through U. After U is deleted, V and W are no longer connected. In turn, if u has only one son, it is clear that the remaining points are connected in T-{u} after deletion, and of course it is also connected in G-{u.
If u is not the root, this is the case. If the descendant of W or W does not connect back to the edge of the U ancestor, all the paths from W to u Father F must pass through U. Note that the undirected graph does not have the f side and the C side, so W and its offspring can only connect back to the ancestor through the B side.
In turn, if every son of u can connect back to F (if the axis can be further connected, he can go down the tree and eventually go to F ), deleting a u will not at least cause the son of u to be disconnected from F. Is it possible that the other two points are not connected? Assume that X and Y exist so that X and Y are not connected after U is deleted. Obviously, there must be at least one descendant of U between x and y. Otherwise, the Child tree X and Y rooted in u can also be connected through the rest of T. In this way, X is the descendant of U. Note that we assume that X can be connected back to F, so the figure should look like one of the following two situations:
In this case, Y is not the descendant of U, so that X first goes to F, then walks to V along the side of T, and does not pass through U.
In Case 2, Y is also the descendant of U, then y can be connected back to F. In this way, X first goes to F and then from F to Y without passing through U.
In both cases, the connectivity between x and y is not caused.
Similar to the cutover, we can define a bridge without a directed graph: If the undirected graph G is no longer connected after an edge e is deleted, the bridge called E is G. It is not difficult to judge the bridge. You only need to judge the t side (u, v. If the descendant of V cannot connect back to the U or U's ancestor, the U and V are not connected after the deletion (u, v. That is, after finding t edge (u, v) and recursively traversing V, if dfn [u] <low [v], (u, v) is the bridge. Similar to cutover, a graph without bridges is called an edge-connected graph. If an undirected graph is edge-connected, you can direct its edge to obtain a strongly connected directed graph.
The definitions of low [u], dfn [u], and idx are the same as those of the previous program. Fa indicates the parent node of the u that is transferred to the U node, CNT indicates that the current node u has a son node, and root indicates the root node of the DFS search tree.
Cut Point and bridge
Void Tarjan (int u, int FA) {int CNT = 0; low [u] = dfn [u] = ++ idx; For (INT I = 0; I <(INT) adj [u]. size (); I ++) {int v = adj [u] [I]; If (V = FA) continue; If (! Dfn [v]) {Tarjan (v, U); ++ CNT; low [u] = min (low [u], low [v]); if (root = u & CNT> 1) | (root! = U & dfn [u] <= low [v]) // determine whether it is a cut point ISAP [u] = 1; if (dfn [u] <low [v]) Cute [++ nume] = edge (u, v); // you can check whether a bridge exists, use an appropriate structure record as needed .} Else low [u] = min (low [u], dfn [v]); // you do not need to judge whether V is in the stack }}
Why does this program not need to judge whether point V is in the stack?
For example:
According to dfn, we can see that the search order is 1-> 2-> 5-> 6 to form a strongly connected component (, 6), so we started to roll back the stack, go back to 1 and start from 3 to 4. If you use dfn [2] to update low [4] directly, you will get low [4] = 2, after becoming smaller, it is no longer equal to dfn [4] and cannot be rolled back. This is not consistent with the formation of a separate strongly connected component in the last 4. Therefore, it is not a point in the stack, it cannot be used to update the low [] value of the current vertex. Why does the undirected graph not need to be labeled? At that time, because the edge is undirected, when an edge exists from 4-> 2, the edge also has 2-> 4.
Because 2 has been marked before, and traversing to the current node 4 does not come through the edge W (2, 4, then there must be another path that connects 2 and 4 (4-3-1-2 in the figure), so that 2 and 4 are dual-connected.
The scale-down of an undirected graph is similar to that of a directed graph. If an undirected graph is converted into a directed graph, a directed edge U-> V is built for each undirected edge (u, v, v-> U.
Of course, there is also an undirected graph approach, just a little changed on the basis of the directed graph.
Void Tarjan (int u, int pre) {sta [top ++] = u; low [u] = dfn [u] = ++ idx; bool flag = 1; for (INT I = 0; I <(INT) adj [u]. size (); I ++) {int v = adj [u] [I]; // If (V = pre & flag) // judge the duplicate edge {flag = 0; continue;} If (! Dfn [v]) {Tarjan (V); low [u] = min (low [u], low [v]);} else if (dfn [v] <low [u]) low [u] = dfn [v]; // Why does [v] Not need to be explained in the stack .} If (low [u] = dfn [u]) {int TMP; ++ SCNT; do {TMP = sta [-- top]; Id [TMP] = SCNT ;} while (TMP! = U );}}
This can also be written in an undirected graph. The preceding statement stipulates that low [u] is the vertex that its children and grandchildren can reach directly through a return ancestor edge. This is changed to the vertex that their children and grandchildren can reach through multiple return ancestor sides continuously. So low [u] = min (low [v], low [u]);
Void Tarjan (int u) {sta [top ++] = u; low [u] = dfn [u] = ++ idx; For (INT I = 0; I <(INT) adj [u]. size (); I ++) {int v = adj [u] [I]; // If (! Dfn [v]) Tarjan (V); low [u] = min (low [u], dfn [v]); // Why does [v] Not need to be explained in the stack .}}
The defect in doing so is that you cannot find a cut point. Multiple return operations may lead to a cut point error. In the multi-ring environment, a line is formed by a single point, in the case that there is only one edge between each two connection points, those connection points should be cut points, but in the DFS process, the edges between these connection points happen to be not the branches and edges, low [u] may record low [u] only when a cut point continuously passes through these cut points to the top of the cut point through multiple return operations.
In this way, the cut points in the middle do not conform to dfn (u) <= low [v.
However, one advantage of this is that all of the edge's dual-connected branches are marked with low, that is, the low of all points belonging to the same dual-connected branch is equal to the same value. In the absence of a bridge, low can return to the highest point (the smallest point of dfn) in the traversal tree along with the branches ).
Returns an undirected graph block.
Void DFS (INT v) {LOW [v] = dfn [v] = ++ itime; STA [top ++] = V; For (INT I = head [v]; i! =-1; I = edge [I]. PRE) {int to = edge [I]. to; If (dfn [to]) low [v] = min (low [v], dfn [to]); // because it is a undirected edge, therefore, else {DFS (to); low [v] = min (low [v], low [to]) is not restricted in the stack. if (dfn [v] <= low [to]) // If the vertices in its subgraphs cannot reach the V ancestor node, it constitutes a connected subgraph. {Block. clear (); While (1) {block. push_back (STA [Top-1]); If (STA [-- top] = to) break; // No v pop-up} block. push_back (V); // although it is not displayed, it is also included in the statistics. Count ();}}}}