Tarjan算法---强联通分量

1、基础知识

      在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。  下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。


      Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。如果发现某节点u有边连到搜索树中栈里的节点v,则更新u的low 值为dfn[v](更新为low[v]也可以)。如果一个节点u已经DFS访问结束,而且此时其low值等于dfn值,则说明u可达的所有节点,都不能到达任何在u之前被DFS访问的节点 那么该节点u就是一个强连通分量在DFS搜索树中的根。此时将栈中所有节点弹出,包括u,就找到了一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。

  Low(u)=MIN{ DFN(u),  Low(v),(u,v)为树枝边,u为v的父节点,DFN(v),(u,v)为指向栈中节点的后向边(非横叉边) }

  当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

2、参考代码

  1 /* Tarjan算法:求一个有向图G=(V,E)里极大强连通分量。
  2   强连通分量是指有向图G里顶点间能互相到达的子图。
  3   如果一个强连通分量已经没有被其它强通分量完全包含,那这个强连通分量就是极大强连通分量*/
  4 /*low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,一个极大强连通分量*/
  5 #include<stdio.h>
  6 #include<stdlib.h>
  7 #define MAXL 50
  8 #define MIN(x,y) ((x) < (y) ? (x) : (y))
  9 
 10 
 11 //结点定义
 12 typedef struct edge_node{
 13     int key;
 14     struct edge_node *next;
 15 }ENode;
 16 typedef struct{
 17     char vertex;
 18     ENode *firstedge;
 19 }VNode;
 20 typedef VNode VList[MAXL];
 21 typedef struct{
 22     VList vlist;
 23     int n,e;
 24 }ALGraph;
 25 int instack[MAXL];  //用于标记是否在栈中
 26 int vis[MAXL];
 27 int dfn[MAXL],low[MAXL];
 28 int depth;
 29 
 30 int ind[MAXL]={0};
 31 
 32 int top;
 33 int stack[MAXL];   //【【【要用到】】】
 34 int num_scc;
 35 
 36 int count_SCCele;
 37 int scc[MAXL];
 38 ALGraph *ALG=(ALGraph *)malloc(sizeof(ALGraph));
 39 
 40 //邻接表生成[有向图]
 41 void creat_ALGraph(ALGraph *ALG)
 42 {
 43     int i,j,k;
 44     char ch1,ch2;
 45     ENode *ep;
 46     
 47     scanf("%d,%d",&ALG->n,&ALG->e);
 48     for(i=0;i<ALG->n;i++) //顶点表
 49     {
 50         getchar();
 51         scanf("%c",&ALG->vlist[i].vertex);
 52         ALG->vlist[i].firstedge=NULL;
 53     }
 54     for(k=0;k<ALG->e;k++)  //边表
 55     {
 56         getchar();
 57         scanf("%c,%c",&ch1,&ch2);
 58         for(i=0;ALG->vlist[i].vertex!=ch1;i++);
 59         for(j=0;ALG->vlist[j].vertex!=ch2;j++);
 60 
 61         ep=(ENode*)malloc(sizeof(ENode));
 62         ep->key=j;
 63         ep->next=ALG->vlist[i].firstedge;
 64         ALG->vlist[i].firstedge=ep;
 65     }
 66 }
 67 
 68 //邻接表输出
 69 void print_ALGraph(ALGraph *ALG)
 70 {
 71     int i;
 72     ENode *ptr=(ENode*)malloc(sizeof(ENode));
 73     for(i=0;i<ALG->n;i++)
 74     {
 75         printf("%c",ALG->vlist[i].vertex);
 76         ptr=ALG->vlist[i].firstedge;
 77         while(ptr!=NULL)  //不能用!ptr
 78         {
 79             printf("->%c",ALG->vlist[ptr->key].vertex);
 80             ptr=ptr->next;
 81         }
 82         printf("\n");
 83     }
 84 }
 85 
 86 //计算初始化用于dfnlow()和bicon()
 87 void init_Tarjan(void)
 88 {
 89     depth=0;
 90     for(int i=0;i<ALG->n;i++)
 91     {
 92         instack[i]=0;
 93         dfn[i]=low[i]=-1;
 94         vis[i]=0;
 95     }
 96 
 97     top=0;  //栈初始化
 98     for(int j=0;j<ALG->n;j++)
 99         stack[j]=-1;
100     num_scc=0;
101 }
102 
103 void init_scc(void)  //scc块初始化
104 {
105     count_SCCele=0;
106     for(int i=0;i<ALG->n;i++)
107         scc[i]=-1;
108 }
109 
110 void SCC_Tarjan(int u)
111 {
112     int son;
113     ENode *ptr=(ENode *)malloc(sizeof(ENode));
114 
115     dfn[u]=low[u]=depth++;  //访问+访问标记+入栈+入栈标记+遍历
116     instack[u]=1;
117     vis[u]=1;
118     stack[top++]=u;
119     ptr=ALG->vlist[u].firstedge;
120     while(ptr!=NULL)
121     {
122         son=ptr->key;
123         if(!vis[son])
124         {
125             SCC_Tarjan(son);
126             low[u]=MIN(low[u],low[son]);
127         }
128         else if(instack[son])  //在栈中
129         {
130             low[u]=MIN(low[u],dfn[son]);
131         }
132         ptr=ptr->next;
133     }
134     if(dfn[u] == low[u])   //若此,以u为根的强连通分量
135     {
136         num_scc++;
137         init_scc();
138         do{
139             top--;
140             scc[count_SCCele++]=stack[top];
141             instack[stack[top]]=0;
142         }while(stack[top] != u);
143 
144         for(int cn=0;cn<count_SCCele;cn++)
145             printf("%c ",ALG->vlist[scc[cn]].vertex);
146         printf("\n");
147     }
148 }
149 
150 int main(void)
151 {
152     creat_ALGraph(ALG);
153     print_ALGraph(ALG);
154 
155     init_Tarjan();
156     for(int i=0;i<ALG->n;i++)   //***可以处理不连通的图,如果连通只需要一次即可,即给定一个root,直接bridge_Tarjan(root,-1)***
157         if(!vis[i])
158             SCC_Tarjan(i);
159 //    SCC_Tarjan(2);  //root可以自定义
160 
161     printf("%d\n",num_scc);
162 
163     printf("%s %s %s\n","ver","dfn","low");
164     for(int l=0;l<ALG->n;l++)
165         printf("%c: %3d %3d\n",ALG->vlist[l].vertex,dfn[l],low[l]);
166 
167     return 0;
168 }
posted @ 2014-08-19 11:37  万箭穿心,习惯就好。  阅读(264)  评论(0编辑  收藏  举报