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 }
来源:https://www.cnblogs.com/wjcx-sqh/p/5929929.html