1. 算法思想

如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。Tarjan算法是用来求有向图的强连通分量的。求有向图的强连通分量的Tarjan算法是以其发明者Robert Tarjan命名的。Robert Tarjan还发明了求双连通分量的Tarjan算法。Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。(很容易理解,定义DFN(u)为节点u搜索的次序编号(时间戳), Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。DFN定义的是按照顺序可以到达的,例如v0是1 ,v1是2, v0可以到达v2。LOW定义的是u可以到达的子树,例如v1在栈中的子树可以到达的次序1,那么v2肯定可以到达v1。所以v1和v2是强连通。如果v0和v1在栈内之间还有v00,v11那么v0,v00,v11,v1之间也是强连通的)。

2. 实例

从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。

tarjan算法_强连通

返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量

tarjan算法_深度优先遍历_02

返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。

tarjan算法_深度优先遍历_03

继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。

tarjan算法_强连通_04

可以看出,采用的是深度优先的遍历,并且呢又给每个阶段增加了两个标记,判断是否是强连通。

3. 代码

4. #define M 5010//题目中可能的最大点数
5. int STACK[M],top=0;//Tarjan算法中的栈
6. bool InStack[M];//检查是否在栈中
7. int DFN[M];//深度优先搜索访问次序
8.
9. int Low[M];//能追溯到的最早的次序
10. int ComponentNumber=0;//有向图强连通分量个数
11. int Index=0;//索引号
12. vector<int> Edge[M];//邻接表表示
13. vector<int> Component[M];//获得强连通分量结果
14. int InComponent[M];//记录每个点在第几号强连通分量里
15. int ComponentDegree[M];//记录每个强连通分量的度
16.
17. void Tarjan(int i)  
18. {  
19. int j;  
20.     DFN[i]=Low[i]=Index++;  
21.     InStack[i]=true;STACK[++top]=i;  
22. for (int e=0;e<Edge[i].size();e++)  
23.     {  
24.         j=Edge[i][e];  
25. if (DFN[j]==-1)  
26.         {  
27.             Tarjan(j);  
28.             Low[i]=min(Low[i],Low[j]);  
29.         }  
30. else
31. if (InStack[j]) Low[i]=min(Low[i],DFN[j]);  
32.     }  
33. if (DFN[i]==Low[i])  
34.     {  
35.         ComponentNumber++;  
36. do{  
37.             j=STACK[top--];  
38.             InStack[j]=false;  
39.             Component[ComponentNumber].  
40.             push_back(j);  
41.             InComponent[j]=ComponentNumber;  
42.         }  
43. while (j!=i);  
44.     }  
45. }