强连通分量是在有向图中才存在的,强连通量是指子图中的最大连通图
Tarjan算法是在DFS树中产生的
图片均来自:[算法]轻松掌握tarjan强连通分量
步骤:
例如简单有向图:
1,首先进行DFS,用数组dfn存储节点的遍历顺序:dfn[a] = 1,dfn[b] = 2 ……
2,接着从遍历最低层 —— d 来迭代可到的的最小层次(low[n]),例如 d 可以回到 b ,
而dfn[b] = 2,小于dfn[d],那么就将 low[d] 设置为2,
3,如果发现无法迭代,则输出,例如回溯到 b 时,b 无法变为更小的数字,那就输出d c b,
d,c,b即为一个连通分量
原理:
右侧为dfs树,左侧为图
利用dfs产生的树,如果最底层节点能回到高层节点,那么他们就连通
代码:
#include <bits/stdc++.h>
using namespace std;
map<string, int> dfn;
map<string, int> low;
map<string, set<string>> graph;
stack<string> stk;
int cnt = 0;
void Tarjan(string u)
{
dfn[u] = low[u] = ++cnt;
stk.push(u);
for(string elem : graph[u])
{
if(dfn.find(elem) == dfn.end())// 如果v未被访问
{
Tarjan(elem);
low[u] = min(low[u], low[elem]);
}
else//如果v在栈中,说明v是u的祖先
{
low[u] = min(low[u], dfn[elem]);
}
}
if(dfn[u] == low[u])
{
string scc_node;
do
{
scc_node = stk.top();
stk.pop();
cout << scc_node << " ";
}while(scc_node != u);
cout << endl;
}
}
int main() {
int n;
cin >> n; // 假设先输入边的数量
for(int i = 0;i < n; ++i)
{
string x, y;
cin >> x >> y;
graph[x].insert(y);
}
for(auto &elem : graph)
{
if(dfn.find(elem.first) == dfn.end()) Tarjan(elem.first);
}
return 0;
}
/*
9
a b
a f
b c
c d
d b
c e
d e
f g
g a
*/
代码具体步骤:
1,赋值:
dfn[u] = low[u] = ++cnt;
stk.push(u);
2,遍历子节点并更新low值
for(string elem : graph[u])
{
if(dfn.find(elem) == dfn.end())// 如果v未被访问
{
Tarjan(elem);
low[u] = min(low[u], low[elem]);
}
else//如果v在栈中,说明v是u的祖先
{
low[u] = min(low[u], dfn[elem]);
}
}
3,如果进入的节点 dfn == low ,那么就证明该输出了
if(dfn[u] == low[u])
{
string scc_node;
do
{
scc_node = stk.top();
stk.pop();
cout << scc_node << " ";
}while(scc_node != u);
cout << endl;
}