Tarjan算法----寻找连通图中的割点

寻找连通图中的割点

谈到求割点最经典的算法就是tarjan。这个算法的思路是利用dfs过程建立一棵深度优先搜索树(dfs tree)。对于一个点u到他所能到达的v,若v未被访问过则这个v与u的连线我们可以称作父子边,

若v已被访问过那我们就把u与v之间的连线成为回边。

由这些父子边与回边组成的“图”我们就称它为深度优先搜索树(dfs tree)。

举个例子

     

图一是一个连通图,图二就是以0为根节点生成的dfs tree。实线是父子边,虚线是回边。

我们观察上图可以看出,根节点只有一个子树,所以不是割点。但我们可以自己在尝试换个图,自己画出对应的dfs tree,会发现根节点要是有两个以上的子树(subtree),那么他必然是一个割点(Aps)。

我们在仔细观察上图还会发现对于一个 non-leaf,non-root node u 的子节点 v,以v为根的子树若没有一条回边指向 u 节点的祖先节点,那么这个u点必然是一个割点(APs)。下图帮助理解这句绕口的话

 

 第一个条件的处理代码实现非常容易,但对于第二个条件的处理就要复杂一些。

先声明两个数组(i是各节点的编号)

disc[ui]  用来维护每个点ui被访问的次序。

low[ui]  用来维护每个以ui节点为根的子树中的vi节点所能到达的最先被访问的点的次序,即 low[u] = min{low[u],low[v]}。若v节点已被访问,说明这个v节点是u节点的祖先,那么low[u] = min{low[u],disc[v]}。

这里low[v]的值比disc[u]小就说明u的子树中存在节点v可以回溯回u的一个祖先节点。

tarjan算法 c++实现

 1 #include<iostream> 
 2 using namespace std;
 3 
 4 int adj[550][550];
 5 int parent[550],disc[550],low[550];
 6 bool visit[550];
 7 bool ap[550];
 8 int n;
 9 
10 void dfs(int u){
11     static int time = 0;
12     int children = 0;
13     visit[u] = true;
14     disc[u] = low[u] = ++time;
15     for(int i=0;i<n;i++){
16         if(adj[u][i] != 0){
17             int v = i;
18             if(!visit[v]){
19                 children++;
20                 parent[v] = u;
21                 dfs(v);
22                 low[u] = min(low[u],low[v]);
23                 if(parent[u] == -1 && children>1){
24                     ap[u] = true;
25                 }
26                 if(parent[u] != -1 && low[v] >= disc[u]){
27                     ap[u] = true;
28                 }
29             }
30             else if(parent[u] != v){
31                 low[u] = min(low[u],disc[v]);
32             }
33         }    
34     }
35 }
36 
37 void init(){
38     for(int i=0;i<n;i++){
39         parent[i] = -1;
40         visit[i] = false;
41         ap[i] = false;
42         disc[i] = 0;
43         low[i] = 0;
44     }
45 }
46 
47 int main(){
48     int m;
49     cin>>n>>m;
50     for(int i=0;i<m;i++){
51         int u,v;
52         cin>>u>>v;
53         adj[u][v] = 1;
54         adj[v][u] = 1;
55     }
56     for(int i=0;i<n;i++){
57         if(ap[i])
58            cout<<ap[i]<<endl;
59     }
60 }

  最后说一下算法的时间复杂度为O(V+E)。dfs的时间复杂度也是O(V+E)。

 

PAT-L013 红色警报  可用这种方法解决。

References:https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/

 

posted @ 2018-03-02 21:18  javier_macro  阅读(982)  评论(1)    收藏  举报