Strong connected component decomposition detailed super detailed

(The writing is a bit small, read it slowly, and you will gain something)
(1)
First of all, we have to understand, what is strong connectivity?
If in a directed graph vertex subset, any two points u and v can find a path from u to v, then the subset is said to be strongly connected

(2)
Secondly, we have to understand, what is a strongly connected component?
If we add any other vertex set to a strongly connected vertex set, it will no longer be strongly connected, then the vertex set is called a strongly connected component

(3)
Finally, we have to understand, what is the decomposition of strongly connected components?
Any directed graph can be decomposed into several disjoint strongly connected components (this disjoint means that the vertices in different components are different), this is the decomposition of strongly connected components

Note: We generally perform strongly connected components on directed and cyclic graphs, because there are no strongly connected components in directed acyclic graphs, and in undirected graphs, all vertex sets are a strongly connected component. This decomposition is meaningless

(4)
How do we decompose strongly connected components?

We first perform a dfs, select any vertex as the starting point, traverse all unvisited vertices, and label the vertices before backtracking (post-order traversal), and repeat the above process for the remaining unvisited vertices. The labeling this time is
mainly It is to make the closer to the end of the graph, the smaller the label of the vertex, paving the way for subsequent operations

How to label?
We can create a vector container so that the closer to the end of the graph we put in the

Why can start from any vertex?
Because we put the tail first. No matter which point we start traversing from, we can traverse to the point closest to the end of the current remaining vertex set, so no matter where we traverse from, we can correctly put this point and all points behind it.
For example, we start traversing in the order of vertices 1 2 3, then the points we put in are the circled parts.
It is easy to see that no matter which point you start from, the operation can be done correctly
Please add a picture description

Why do we need postorder traversal? (That is, recurse downward first, and put in the vector when backtracking)
because the closer we are to the end of the graph, the more we put it in first.
If it is a pre-order traversal, it is equivalent to putting in the closer to the head, which will cause an error, because we start traversing from any vertex, for example, we traverse in the order of 1 2 3, so that we put it first is equivalent to vertex 1, And vertex 1 is not the head, so it will be wrong

Let's do dfs again, first reverse all the edges, and then start dfs with the vertex with the largest label. The set of vertices traversed by dfs each time constitutes a strongly connected component. Take an array to save which strong connection each of the following points belongs to connected components

Reverse, in fact, is to record one more reverse edge when recording an edge
. Traversing from the largest label is actually traversing the vector from the back to the front.

The idea of ​​this is this:
because we are traversing from head to tail, there are only two cases we are traversing
: if we traverse to vertex 1, the ones that have not been traversed at this time may be "beside", or "Behind"
(the shadow is equivalent to having been traversed)
For "beside": Since the upstream point has been traversed, the next point is not recursive, so don't consider it.
For "behind": suppose vertex v is in the " "behind", because it is "behind", so there is a path from u to v. If the edge is reversed, there is still a path from vertex u to vertex v, which is equivalent to a path from v to u in the forward graph. Prove u to v connected

Please add a picture description

code show as below:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
vector<int> data[10005];
vector<int> rdata[10005];
vector<int> flag;
int used[10005];
int kind[10005];
void add_edge(int i, int j)
{
    
    
    data[i].push_back(j);
    rdata[j].push_back(i);
}
void dfs(int v)
{
    
    
    used[v] = true;
    for(int i = 0; i < data[v].size(); i++)
    {
    
    
        if(!used[data[v][i]])
        {
    
    
            dfs(data[v][i]);
        }
    }
    flag.push_back(v);
}
void rdfs(int v, int k)
{
    
    
    used[v] = true;
    kind[v] = k;
    for(int i = 0; i < rdata[v].size(); i++)
    {
    
    
        if(!used[rdata[v][i]])
        {
    
    
            rdfs(rdata[v][i], k);
        }
    }
}
int scc()
{
    
    
    memset(used, false, sizeof(used));
    for(int i = 0; i < N; i++)
    {
    
    
        if(!used[i]) dfs(i);
    }
    memset(used, false, sizeof(used));
    int num = 0;
    for(int i = flag.size() - 1; i >= 0; i--)
    {
    
    

        if(!used[flag[i]])
        {
    
    
            rdfs(flag[i], num);
            num++;
        }
    }
	return num
}

Guess you like

Origin blog.csdn.net/m0_52212261/article/details/121154007