随笔-80  评论-24  文章-0  trackbacks-0
有向图强连通分支就是这样一个最大的顶点集合C,其中的任意一对顶点u和v,u可达v,并且v可达u。
kosaraju算法是求有向图强连通分支的有效算法。起基本思想就是两次DFS,说起来简单,但是如何证明其两次DFS是能成功求出强连通分支的呢?
1、对图G第一次DFS遍历完成之后,会形成若干个DFS树,它们组成森林,假设有C1,C2,C3...Ck棵DFS树,而且遍历完成时间C1<C2<C3<...<Ck,这样,可知必然不存在从Ci到Cj的边(其中i<j),因为如果存在Ci->Cj则假设这条边是从Ci的x节点到Cj的y节点,则由白色路径定理可知,必然存在一条从Ci到Cj的白色路径,则Cj会是Ci的一棵子树!所以不存在从Ci到Cj的边(其中i<j)。
2、当把图G的所有边逆向之后,设形成图F,则由1可知不存在从Cj到Ci的路径,这样,对图F按照第一次DFS遍历时各个节点的完成时间由大到小进行DFS遍历,即从Ck的树根r开始遍历:首先根据前面的论证需要明确一个事实,r所在的强连通分支的点集必然属于树Ck,因为不存在从Ck到Ca(a<k)的边,另外就是r到Ck中所有节点都是可达的,这样当对图G的逆图F从r点开始遍历的时候,所有可到达的点在图G中都是可到达r的!这样遍历结果就是r所在的强连通分支!然后在从剩下没有遍历过的所有节点中第一次DFS的完成时间最大的点开始遍历,因为剩下的点都为白色(可能会存在白色点指向从r形成的强连通分支的反向边),所以与Ck的根r类似,同样能形成一个强连通分支,这样就能把Ck中的所有强连通分支找出,同样可以找出Ck-1...C1中的强连通分支。
证毕!
证明如果能看懂的话那代码就非常简单了,下面是示例代码:
 1 #include <cstdio>
 2 #include <vector>
 3 
 4 #define MAXN 105
 5 #define ROOT 0
 6 
 7 typedef struct {
 8   std::vector<int> adj_list;
 9 #define WHITE 0
10 #define GREY 1
11 #define BLACK 2
12   int color;
13 } GRAPH;
14 
15 GRAPH graph[MAXN];
16 GRAPH rgraph[MAXN];
17 int nr_of_nodes = 0;
18 int nr_of_edges = 0;
19 
20 static void init() {
21   int i;
22   for (i = 0; i < MAXN; ++i) {
23     graph[i].adj_list.clear();
24     graph[i].color = WHITE;
25     rgraph[i].adj_list.clear();
26     rgraph[i].color = WHITE;
27   }
28   nr_of_nodes = 0;
29   nr_of_edges = 0;
30 }
31 
32 static void input() {
33   int i, u, v;
34   freopen("input""r", stdin);
35   scanf("%d%d"&nr_of_nodes, &nr_of_edges);
36   for (i = 0; i < nr_of_edges; ++i) {
37     scanf("%d%d"&u, &v);
38     graph[u].adj_list.push_back(v);
39     rgraph[v].adj_list.push_back(u);
40   }
41 }
42 
43 static void DFS(GRAPH* g, int x, std::vector<int>* list) {
44   int i;
45   g[x].color = GREY;
46   for (i = 0; i < g[x].adj_list.size(); ++i) {
47     if (g[g[x].adj_list[i]].color == WHITE) {
48       DFS(g, g[x].adj_list[i], list);
49     }
50   }
51   g[x].color = BLACK;
52   list->push_back(x);
53 }
54 
55 static void strong_connected_components() {
56   int i, j;
57   std::vector<int> vertexes
58   std::vector<int> scc;
59   std::vector<std::vector<int> > sccs;
60 
61   for (i = 0; i < nr_of_nodes; ++i) {
62     if (graph[i].color == WHITE) {
63       DFS(graph, i, &vertexes);
64     }
65   }
66 
67   for (i = vertexes.size() - 1; i >= 0--i) {
68     if (rgraph[vertexes[i]].color == WHITE) {
69       DFS(rgraph, vertexes[i], &scc);
70       sccs.push_back(scc);
71       scc.clear();
72     }
73   }
74 
75   for (i = 0; i < sccs.size(); ++i) {
76     printf("strong connected component %d: ", i);
77     for (j = 0; j < sccs[i].size(); ++j) {
78       printf("%d ", sccs[i][j]);
79     }
80     printf("\n");
81   }
82 }
83 
84 int main(int argc, const char **argv) {
85   init();
86   input();
87   strong_connected_components();
88   return 0;
89 }
90 
91 

核心部分代码就是strong_connected_components()和DFS()。这里利用一个栈来保存每个节点的完成先后顺序,第二次遍历的时候就按照最后完成的节点开始向前遍历。
posted on 2012-08-22 21:28 myjfm 阅读(1100) 评论(0)  编辑 收藏 引用 所属分类: 算法基础

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理