参考博客:比较详细。
http://www.byvoid.com/blog/scc-tarjan/
算法伪代码描述:Tarjan(root)
1、初始化 def , low[], map[][]
2、stack<- root // 将 root 节点入栈
3、 对 与 root 相连的每个 节点 p 如果 p 在 stack 中 那么 low[root] = min(low[root], def[p]);并且 call Tarjan(p)
4、 如果对 与 root 相连的每个 节点 p 如果 p 不在 stack 中 那么 low[root] = min( low[p], low[root])
5 、当 low[root] = def[roo] 时,输出栈里 在 root 之上的 所以 元素 为一个连通分量
6、继续搜索,直到所有的节点被遍历
代码如下:
1:邻接矩阵存储
#include <iostream>
#include<stack>
#include<vector>
using namespace std;
#define V 100
int map[V][V]; // 存放地图
int def[V]; // def[i] 记录 i 节点的访问时间
int low[V]; // 记录 i 节点连通分量 所在集合的能追朔到的最小 def
bool instack[V];// 标记 i 是否在 堆中, 在 为 1 , 不在 为 0
int count ;// 连通分量的个数
int temp;//
vector<int> result[V];// 保存连通分量的结果集
stack<int> stack_v ;
int index = 0;
int min ( int a, int b){
return a>b?b:a;
}
void tarjan ( int map[][V], int def[V], int low[V],int root, int n){// n 表示节点的个数
instack[root] = true;
def[root] = low[root] = ++index;
stack_v.push(root);
//假设初始化已经完成
// 从根节点 root (也就是 入度为 0 的节点进行 搜素)
for ( int i = 1; i <= n; i++){
if( map[root][i] == 1){// 表示 root 和 节点 i 连通
if( def[i] == 0 ){ // 表示 i 点还没有被访问
tarjan(map,def,low,i,n);
low[root] = min( low[root],low[i]);
}
if( instack[i] == true){ // 如果 i 节点在 栈中
low[root] = min( low[root], def[i]);
}
}
}
if( def[root] == low[root] ){
count ++;
do{
temp = stack_v.top();
stack_v.pop();
instack[temp] = false;
cout<< temp<<" ";
result[count].push_back(temp);
}while(temp != root);
cout<<endl;
}
}
int main(void){
int n ,e, x, y;// n 为顶点个数, e 为表的个数
cin>>n>>e;
for( int i =0; i < e; i++){
cin>>x>>y;
map[x][y] = 1;
}
tarjan(map,def,low,1,n);
cout<<count<<endl;
return 0;
}
2:邻接表存储
#include<iostream>
#include<vector>
using namespace std;
#define M 2000 //题目中可能的最大点数
int STACK[M],top=0; //Tarjan 算法中的栈
bool InStack[M]; //检查是否在栈中
int DFN[M]; //深度优先搜索访问次序
int Low[M]; //能追溯到的最早的次序
int ComponetNumber=0; //有向图强连通分量个数
int Index=0; //索引号
vector <int> Edge[M]; //邻接表表示
vector <int> Component[M]; //获得强连通分量结果
int min(int a, int b){
return a>b?b:a;
}
void Tarjan(int i)
{
int j;
DFN[i]=Low[i]=Index++;
InStack[i]=true;
STACK[++top]=i;
for (int e=0;e<Edge[i].size();e++)
{
j=Edge[i][e];
if (DFN[j]==-1) //如过点 j 还没有被访问
{
Tarjan(j);
Low[i]=min(Low[i],Low[j]);
}
else if (InStack[j]) // 如果点j 在
Low[i]=min(Low[i],DFN[j]);
}
if (DFN[i]==Low[i])
{
cout<<"TT "<<i<<" "<<Low[i]<<endl;
ComponetNumber++;
do
{
j=STACK[top--];
InStack[j]=false;
Component[ComponetNumber].push_back(j);
}
while (j!=i);
}
}
void solve(int N) //此图中点的个数,注意是0-indexed!
{
memset(STACK,-1,sizeof(STACK));
memset(InStack,0,sizeof(InStack));
memset(DFN,-1,sizeof(DFN));
memset(Low,-1,sizeof(Low));
for(int i=0;i<N;i++)
if(DFN[i]==-1)
Tarjan(i);
}
/*
此算法正常工作的基础是图是0-indexed的。
*/
int main()
{
Edge[0].push_back(1);Edge[0].push_back(2);
Edge[1].push_back(3);
Edge[2].push_back(3);Edge[2].push_back(4);
Edge[3].push_back(0);Edge[3].push_back(5);
Edge[4].push_back(5);
int N=6;
solve(N);
cout<<"ComponetNumber is "<<ComponetNumber<<endl;
for(int i=0;i<N;i++)
cout<<Low[i]<<" ";
cout<<endl;
for( i=0;i<N;i++)
{
for(int j=0;j<Component[i].size();j++)
cout<<Component[i][j];
cout<<endl;
}
return 0;
}