( 图论专题 )【 强联通分量Tarjan 】

( 图论专题 )【 强联通分量Tarjan 】

推荐阅读:https://blog.csdn.net/weixin_43843835/article/details/88381828

推荐阅读:https://blog.csdn.net/qq_34374664/article/details/77488976

强连通(strongly connected): 在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,b)强连通。


强连通图: 如果 在一个有向图G中,每两个点都强连通,我们就叫这个图,强连通图。


强连通分量(strongly connected components):在一个有向图G中,有一个子图,这个子图每2个点都满足强连通,我们就叫这个子图叫做 强连通分量 [分量 : 把一个向量分解成几个方向的向量的和,那些方向上的向量就叫做该向量(未分解前的向量)的分量]
举个简单的栗子:

比如说这个图,在这个图中呢,点1与点2互相都有路径到达对方,所以它们强连通.

而在这个有向图中,点1 2 3组成的这个子图,是整个有向图中的强连通分量。


利用Tarjan算法求强连通分量

Tarjan算法的基本思路
首先考虑强连通分量的性质,即存在一条回路能从初始点又回到初始点。在这个查找的过程中,可以对经过的结点标记,当发现某一节点连向的点正好以及被标记过,则说明找到了一条回路,而这个回路上的所有点构成一个强连通分量。为了保存这个强连通分量,我们需要知道这条路上有哪些点,而此时,栈就是一种适合该算法的数据结构。对于每次搜索的点,我们都加入栈中,遇到回路时,在把栈中的元素逐个弹出,记录它们的起始结点,直到栈中弹出的元素正好是起始结点时,结束弹栈,继续搜索其它强连通分量。在这个过程中,所有的点和都有的边都被遍历了一次,所以最终的时间复杂度为O(N+E)
O(N+E)

Tarjan算法的实现
为了实现这个过程,Tarjan算法需要装备如下几样东西:
记录搜索顺序的数组 dfn;
记录所属强连通的数组 low;
表示某结点是否在栈中的数组 via;
一个栈存储搜索路径;
从上面对Tarjan算法的描述中,很容易看出这个算法是基于深度优先搜索实现的。接下来,对Tarjan算法进行逐步推演。
(本人不喜欢使用网上用烂的素材,接下来包括以前的讲解图都是自己手画的,不喜勿喷)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码:

#include <bits/stdc++.h>

using namespace std;

vector<int> G[1001];
vector<int> belong[1001];
int dfn[1001],low[1001];
int via[1001];
int cnt,tot;
stack<int> s;

void tarjan( int x )
{
    dfn[x] = low[x] = ++cnt;
    s.push(x);
    via[x] = 1;
    for ( int i=0; i<G[x].size(); i++ ) {
        int y = G[x][i];
        if ( dfn[y]==0 ) { // 如该没被访问过
            tarjan(y);
            low[x] = min(low[x],low[y]);
        }
        else if ( via[y]==1 ) { // 如果已经被访问过
            low[x] = min(low[x],dfn[y]);
        }
    }
    if ( low[x]==dfn[x] ) {  // 发现是整个强连通分量子树里的最小根。
        tot ++;
        int node;
        do {
            node = s.top(); s.pop();
            via[node] = 0;
            belong[tot].push_back(node);
        } while(node!=x);

    }
}

int main()
{
    int n,m,u,v;
    cin >> n >> m;
    memset(dfn,0,sizeof(dfn));
    cnt=0; tot=0;
    for ( int i=0; i<m; i++ ) {
        cin >> u >> v;
        G[u].push_back(v);
    }
    for ( int i=1; i<=n; i++ ) {
        if ( dfn[i]==0 ) tarjan(i);
    }
    for ( int i=1; i<=tot; i++ ) {
        cout << "SCG:" << i << endl;
        for ( int j=0; j<belong[i].size(); j++ ) {
            cout << belong[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

 

按照先前的推演图,生成测试样例

7 11
1 2
2 3
2 5
2 4
3 5
3 7
7 5
5 6
6 7
4 1
4 5

运行结果与推演时一致
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值