tarjan算法求关节点

求无向图的关节点

dfn[]来存点的深度数

在一张深度优先搜索树中,如果u和v是两个顶点,且生成树中u是v的祖先,则必有dfn[u]<dfn[v],表明u先被访问。

回边:原图中除了dfs树中的边外,还有的边叫回边;如:1->2,1->3,2->3中,dfs时1->2->3,这里dfs树中的边为1->2,2->3,那么1->3就是回边;

(1)如果点u是dfs生成树的根,那么u至少有2个子女。理由:因为一删除u,它的子女所在的子树就断开了。

(2)如果点u不是dfs生成树的根,那么u的一个子女w,从w或w的子孙出发或回边到达u的祖先。如果能够到达,那么点u就不是关节点了。

因此,可以对每个店v设一个新的数组low[],来保存通过v能够到达的最低深度数。

low[v]=min{

  dfn[u] 

  min(low[w]|w为点v的子孙)  

  min(dfn[x]|x和v构成回边)

};

因此顶点u是关节点的充要条件为:u为根结点时,有2个或以上的子女。u不是根结点的时候,它的一个子女w,有low[w]>=dfn[u]。

技术分享

low[w]>=dfn[u]表示顶点u的子女w,能够到达的最低深度数大于等于顶点u的深度数。及w的子女没有不存在指向u祖先的边。这是删除顶点u,u的子树就从dfs树中脱离了。

如上图中点6是关节点,因为点6的子女结点low[w]>=dfn[u],及不存在能够指向u祖先的回边。删除点6后,子树断开。

而点7不是关节点,因为点7的子女结点点8,存在一条回边,满足low[8]<dfn[7],

删除点7后,子树未断开。

poj1523

技术分享
  1 #include<stdio.h>
  2 #include<string.h>
  3 int map[1003][1003];
  4 int vis[1003];//标记
  5 int dfn[1003];//来存他的dfs树中深度数
  6 int depth;//深度
  7 int subnets[1003]//删除该点后存其余能连通多少
  8 int nodenum;//点的个数
  9 int son;//根结点子女个数
 10 int low[1003];//存该点能够到达最低深度数
 11 void inin()
 12 {
 13     int i,j;
 14     memset(vis,0,sizeof(vis));
 15     memset(subnets,0,sizeof(subnets));
 16     depth=1;//开始时深度为1
 17     low[1]=dfn[1]=1;//根结点的深度为1
 18     son=0;
 19     vis[1]=1;
 20 }
 21 int min(int x,int y)
 22 {
 23     return x<y?x:y;
 24 }
 25 void dfs(int u)
 26 {
 27     int i,j;
 28     for(i=1;i<=nodenum;i++)
 29     {
 30         if(map[u][i])//如果两点连通
 31         {
 32             if(!vis[i])//如果之前为访问,即不是回边
 33             {
 34                 vis[i]=1;
 35                 depth++;//深度+1
 36                 dfn[i]=low[i]=depth;//更新
 37                 dfs(i);//继续搜索
 38                 low[u]=min(low[u],low[i]);//low[i]已在之前的dfs中更新,low[]存能够到达的最低深度数,所以要比较它本身和它的子女
 39                 if(low[i]>=dfn[u])//如果该结点为关节点
 40                 {
 41                     if(u==1)son++;//如果为根结点,son记录有几个子女
 42                     if(u!=1)subnets[u]++;//如果不是根结点,更新删除它后有多少的连通*分量*
 43                 }
 44             }
 45             else//如果之前已经被访问过,表明存在回路
 46             {
 47                 low[u]=min(low[u],dfn[i]);//更新low[]
 48             }
 49         }
 50     }
 51 }
 52 int main()
 53 {
 54     int i,j,u,v,flag,ff=1;
 55     while(1)
 56     {
 57         //输入部分
 58         nodenum=0;
 59         memset(map,0,sizeof(map));
 60         scanf("%d",&u);
 61         if(u==0)break;
 62         scanf("%d",&v);
 63         if(v>nodenum)nodenum=v;
 64         if(u>nodenum)nodenum=u;
 65         map[u][v]=map[v][u]=1;
 66         while(1)
 67         {
 68             scanf("%d",&u);
 69             if(u==0)break;
 70             scanf("%d",&v);
 71             if(v>nodenum)nodenum=v;
 72             if(u>nodenum)nodenum=u;
 73             map[u][v]=map[v][u]=1;
 74         }
 75         //初始化
 76         inin();
 77 
 78         dfs(1);//建树
 79         if(son>1)//如果根结点是关节点
 80         {
 81             subnets[1]=son-1;//更新subnets[1],即关节点的子女个数
 82         }
 83 
 84         if(ff>1)
 85             printf("\n");
 86         printf("Network #%d\n",ff++);
 87 
 88         flag=0;
 89         for(i=1;i<=nodenum;i++)
 90         {
 91             if(subnets[i])//如果连通分量存在,表示该点位关节点
 92             {
 93                 flag=1;
 94                 printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);//subnets[]+1是因为还有分成两块,一个点;
 95             }
 96         }
 97         if(!flag)
 98             printf("  No SPF nodes\n");
 99     }
100 }
View Code

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。