( 图论专题 )【 强联通分量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
运行结果与推演时一致