Tarjan强连通分量
强连通分量(Tarjan)
个人认为:求强连通分量的过程就是缩点的过程
算法主要步骤
时间戳
点
和它子树中的返祖边和横插边能连到还没有出栈的
的最小的点
- 在
的时候维护一个栈,第一次访问某一个节点时就把这个点加入到栈中,当一个点
的
时,它就是这个强连通分量在搜索树中最高的点,将栈里的点出栈,知道点
也出栈,这些点构成一个强连通分量
一些思考
- 当
时,它就不能通过返祖边或者横插边去到比它更靠上的点了
- 理解它的正确性:
- 在dfs中,先出栈的点一定能被后出栈的点到达
- 越是靠后出栈就越有可能到达不了 (
)
- 所以当访问到了一个
的点时,一定是通过横插边或者返祖边回到了之前某个点,也就组成了一个强连通分量
代码
const int maxn = 1e5;
int dfn[maxn], low[maxn];
int col[maxn]; // 每个点属于哪一个强连通分量
bool vis[maxn]; // 是否在我维护的栈中
int cnt = 0;
int num = 0; // 有几个强连通分量
std::stack<int> stk;
std::vector<int> edge[maxn];
void tarjan(int x)
{
dfn[x] = low[x] = ++cnt;
vis[x] = 1;
stk.push(x);
for (int i = 0; i < edge[i].size(); i++)
{
int y = edge[x][i];
if (dfn[y] == 0) // 树边
{
tarjan(y);
low[x] = std::min(low[x], low[y]);
}
else if (vis[y] == 1) // 在栈中才有效 返祖边或横插边
{
low[x] = std::min(low[x], dfn[y]);
}
}
if (low[x] == dfn[x])
{
num++;
while(true)
{
int top = stk.top();
col[top] = num;
vis[top] = 0;
stk.pop();
if (x == top) break;
}
}
}