Tarjan(寻找强连通分量)

强连通分量是在有向图中才存在的,强连通量是指子图中的最大连通图

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;
    }
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值