割点和桥---Tarjan算法

使用Tarjan算法求解图的割点和桥。

1、割点

     主要的算法结构就是DFS,一个点是割点,当且仅当以下两种情况:
        (1)该节点是根节点,且有两棵以上的子树;
        (2)该节点的任一子节点,没有到该节点祖先的反向边(就是说如果没有这个割点,那么这个子节点和那个祖先之间不连通);

 1 void cutpoint_Tarjan(int u,int parent)
 2 {
 3     int son;  //节点m的儿子节点
 4     ENode *ptr=(ENode *)malloc(sizeof(ENode));
 5 
 6     dfn[u]=low[u]=depth++;  //访问+标记+遍历
 7     vis[u]=1;
 8     ptr=ALG->vlist[u].firstedge;
 9     while(ptr!=NULL)
10     {
11         son=ptr->key;
12         if(!vis[son])
13         {
14             DFS(son,u);
15             low[u]=MIN(low[u],low[son]);
16 
17             if(u==root)    //不同之处//根节点[要定义初始访问节点,因为要考虑割点的2个判断条件]
18                 cut[u]++;
19             else if(u!=root && dfn[u] <= low[son])
20                 cut[u]++;  //m是割点 
21         }
22         else if(son != parent)  //有后向边
23         {
24             low[u]=MIN(low[u],dfn[son]);
25         }
26         ptr=ptr->next;
27     }
28 }

2、桥

Tarjan算法求割边(桥):
【1】使用(son!=parent && dfn[son]<dfn[u]);

 1 void init_Tarjan(void)
 2 {
 3     depth=0;
 4     for(int i=0;i<ALG->n;i++)
 5     {
 6         dfn[i]=low[i]=-1;
 7         vis[i]=0;
 8     }
 9 
10     num_bridge=0;
11     for(int j=0;j<ALG->e;j++)
12     {
13         bridge_Node[j].front=0;
14         bridge_Node[j].rear =0;
15     }
16 }
17 
18 void Add_to_BNode(int front,int rear)  //从坐标1开始存储
19 {
20     bridge_Node[num_bridge].front=front;
21     bridge_Node[num_bridge].rear =rear;
22 }
23 
24 void bridgenode_Tarjan(int u,int parent)
25 {
26     int son;
27     ENode *ptr=(ENode*)malloc(sizeof(ENode));
28 
29     dfn[u]=low[u]=depth++;  //访问+标记+遍历
30     vis[u]=1;
31     ptr=ALG->vlist[u].firstedge;
32     while(ptr!=NULL)
33     {
34         son=ptr->key;
35         if(son!=parent && dfn[son]<dfn[u])  //避免走重边,效果和id一样
36         {
37             if(!vis[son])
38             {
39                 bridge_node_Tarjan(son,u);
40                 low[u]=MIN(low[u],low[son]);
41                 if(low[son] > dfn[u])  //(u,son)是桥
42                 {
43                     num_bridge++;
44                     Add_to_BNode(u,son);  //存储桥            
45                 }
46             }
47             else if(son != parent)
48             {
49                 low[u]=MIN(low[u],dfn[son]);
50             }
51         }
52         ptr=ptr->next;
53     }
54 }

【2】为每一条边标号 id记录每条边(一条无向边拆成的两条有向边id相同),每个点的父亲到它的边的标号;

 1 //结点定义  /*****注意边表节点定义有所变化****/
 2 typedef struct edge_node{
 3     int key;   //儿子节点[边的终点]
 4     int id;    //边的编号
 5     struct edge_node *next;
 6 }ENode;
 7 void init_Tarjan(void)  //Tarjan算法初始化
 8 {
 9     depth=0;
10     for(int i=0;i<ALG->n;i++)
11     {
12         vis[i]=0;
13         dfn[i]=low[i]=-1;
14     }
15     count_bridge=0;
16     for(int j=1;j<=ALG->e;j++)  //取值于1-e
17         bridge[j]=0;
18 }
19 void bridge_Tarjan(int u,int id)  //id是u的父亲边的编号
20 {    
21     int son;  //u的儿子节点
22     ENode *ptr=(ENode *)malloc(sizeof(ENode));
23 
24     dfn[u]=low[u]=depth++;  //访问+标记+遍历
25     vis[u]=1;
26     ptr=ALG->vlist[u].firstedge;
27     while(ptr!=NULL)
28     {
29         if(ptr->id != id)  //避免走重边,相当于cutpoint_Tarjan中的(son != parent)
30         {
31             son=ptr->key;
32             if(!vis[son])
33             {
34                 bridge_Tarjan(son,ptr->id);
35                 low[u]=MIN(low[u],low[son]);
36                 if(dfn[u] < low[son])   //注意不取等号,当DFN[u]==LOW[v]时,当u->v dfs递归,存在一条v->u的回边,使得LOW[v]=DFN[u];故不为桥
37                 {
38                     bridge[ptr->id]=1;  //第id边是桥
39                     printf("(%c,%c) ",ALG->vlist[u].vertex,ALG->vlist[son].vertex);  //用于输出割边
40                 }
41             }
42             else
43             {
44                 low[u]=MIN(low[u],dfn[son]);
45             }
46         }
47         ptr=ptr->next;
48     }
49 }
---  纵使山重水复,亦会柳暗花明   sunqh1991@163.com   欢迎关注,互相交流
原文地址:https://www.cnblogs.com/wjcx-sqh/p/5929926.html