傻子都能看懂的求强联通分量(Tarjan算法)
Strong Connected Component 强联通分量
Tarjan算法思想:
一次找到一个强连通分量内的所有点
该算法充分利用了深度优先搜索的顺序性 以及 对以及确保处理完的强连通分量不会干扰后面求强连通分量的过程
记(x,y)为 从x 到 y 的一条边
记dfn为深度优先遍历的时间戳
时间戳就是dfs到这个点的次序
记low(u) 为时间戳为u的点所能到达的在栈内最小时间戳的点
下面上代码!
//Tarjan求有向图强连通算法---时间复杂度O(n)---
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e4+10,M = 1e5+5;
int h[N],ne[M],e[M],idx;
vector<int> scc[N];
int cnt;//记录总共连通块的数量
int id[N];//记录每一个点所在联通块的编号
int n,m;
int dfn[N],low[N],timestep;
bool st[N];//标记每一个点的状态
int stack[N],top;//手写栈
int num[N];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++timestep;//标注时间戳
st[u] = 1;//表示u在栈内
stack[++top] = u;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(!dfn[j])//如果还没有被访问过--访问
{
tarjan(j);
low[u] = min(low[u],low[j]);//用子节点的low值更新
}
else if(st[j])//如果在栈内则用它的值更新
low[u] = min(low[u],low[j]);
}
if(dfn[u] == low[u])//到达强连通块的根点
{
++cnt;
int y;
do
{
y = stack[top--];
st[y] = 0;
id[y] = cnt;
scc[cnt].push_back(y);
num[cnt]++;
}while(y!=u);
}
}
int main()
{
cin>>n>>m;
int a,b;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
return 0;
}
建议把下面的图自己跑一遍//中括号是low