有向连通分量的Tarjan算法

参考博客:比较详细。

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;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值