这个是个最简单的实现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; }