这个是个最简单的实现tarjan算法的程序了,主要是为了了解这个算法,以及中间的过程,
因为邻接矩阵写的话,简单明了,适用于理解算法。当然对邻接表,节点是单一一个数字或字符的,实现也比较简单
改天我会再写个用邻接表的,节点是字符串的。那样的话,程序更具有广泛性。使用范围就更广了。
这里的节点字符单一,对于是字符串的节点的图的话,明显不具有解答能力。
可以用一种方法,对字符串标序,并且字符串与序号绑定起来。我有一篇文章就是讲用这个方法实现DFS和BFS,节点为字符串的的程序
tarjan算法的步骤:
step 1:对图中的点深搜,
2:在深搜过程中,对每个节点进行标记,也就是记起来这个节点是第几个搜到的
3:判断每个点它能通过回边所到达的标记序号最小的点,然后在回溯时对每个节点进行一次比较,存下标记序号最小的
4:如果标记序号和它所能搜到的序号最小的点的那个序号相等,那么就将栈里在它上面的节点全部输出,它自己也输出,也都退栈。
5: 继续回溯,深搜。
下面贴一下代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
int map[N][N];//存入图,这里是用邻接矩阵,
//假如要用邻接表的话,如果节点也是单一的一个数字,也蛮简单,
//这里就不写了,如果有时间我再写个邻接表的,节点是字符串的来
int dist[N];//这个就是标记数组,表示此时这个节点是否在栈中
int dfn[N],low[N];//第一个是存搜索顺序的数组,
//第二个是存此时这个点所能到达搜索顺序最小的节点的dfn,即那点的序号
int z[N];//这里是用这个数组模拟栈
int n,m,t=0,top=-1;//n表示节点的个数,m表示边的数量,t是用来初始化dfn,和low的
//top就是代表栈顶
void init()//初始化
{
int i, j, r;
memset(map,-1,sizeof(map));
memset(dist,0,sizeof(dist));
memset(dfn,0,sizeof(dfn));
for(j=1; j<=m; j++)//输入边
{
scanf("%d%d",&i,&r);
map[i][r]=1;
}
}
void tarjan(int k)//tarjan算法的实现,
{
int i;
dfn[k]=low[k]=++t;//标记搜索序号,并将low也赋同样的值
z[++top]=k;//将k入栈
dist[k]=1;//标记k已在栈中
for(i=1; i<=n; i++)//dfs
{
if(map[k][i]!=-1)
{
if(dfn[i]==0)//i没入栈,这里其实也可以改为dist[i]==0,效果是一样的
{//dfn的话因为在经过tarjan里会被赋值,所以就不会为0,没经过的话就代表没搜过
tarjan(i);//那么就要搜它
if(low[k]>low[i])low[k]=low[i];//将所能达到的最小的搜索序赋给low[k]
}
else if(dist[i]==1&&low[k]>dfn[i])//如果在栈中,就直接比较,存最小
{
low[k]=dfn[i];
}
}
}
if(low[k]==dfn[k])//如果相等那么就要输出这一个强连通分量
{
do
{
printf("%d ",z[top]);
dist[z[top]]=0;
}
while(z[top--]!=k);
printf("\n");
}
return ;
}
int main()
{
int i;
scanf("%d %d",&m,&n);
init();
for(i=1; i<=n; i++)
if(!dfn[i])//没有经过tarjan搜索过
tarjan(i);//那么就搜它
return 0;
}