tarjan算法应用之割边和割点

来源:互联网 发布:js调用支付宝api接口 编辑:程序博客网 时间:2024/05/21 08:57


割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。


树边:在搜索树中的蓝色线所示,可理解为在DFS过程中访问未访问节点时所经过的边,也称为父子边
回边:在搜索树中的橙色线所示,可理解为在DFS过程中遇到已访问节点时所经过的边,也称为返祖边、后向边
观察DFS搜索树,我们可以发现有两类节点可以成为割点。对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;对非叶子节点u(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。对于根结点,显然很好处理;但是对于非叶子节点,怎么去判断有没有回边是一个值得深思的问题。我们用dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么low[u]的计算过程如下。


对于给的例子,其求出的dfn和low数组如下。
id     123456
dfn   123456
low   111444
可以发现,对于情况2,当(u,v)为树边且low[v]≥dfn[u]时,节点u才为割点。而当(u,v)为树边且low[v]>dfn[u]时,表示v节点只能通过该边(u,v)与u连通,那么(u,v)即为割边。tarjan算法的时间复杂度是O(n+m)的,非常快。
以hihoCoder1183为例给出代码:

#include<cstdio>#include<vector>#include<algorithm>using namespace std;int n,m,order=0;int low[20004],dfn[20004],father[20004],son[20004];//father:父结点 son:子结点个数 vector<int> cutpoint,edge[20004];vector< pair<int,int> > cutedge;void tarjan(int u){dfn[u]=low[u]=++order;bool flag=false;for (int i=0;i<edge[u].size();i++){int v=edge[u][i];if(!dfn[v]){son[u]++;father[v]=u; tarjan(v);if(low[v]>=dfn[u]) flag=true;//点u为割点 if(low[v]>dfn[u]) cutedge.push_back(make_pair(min(v,u),max(v,u)));//边v-u为割边 low[u]=min(low[u],low[v]);}else if(v!=father[u]) low[u]=min(low[u],dfn[v]);}//根节点若有两棵或两棵以上的子树则该为割点//非根节点若所有子树节点均没有指向u的祖先节点的回边则为割点if((father[u]==0&&son[u]>1)||(father[u]&&flag)) cutpoint.push_back(u);}int main(){scanf("%d%d",&n,&m);for (int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);edge[u].push_back(v),edge[v].push_back(u);}tarjan(1);sort(cutedge.begin(),cutedge.end());sort(cutpoint.begin(),cutpoint.end());if(0==cutpoint.size()) puts("Null");else{printf("%d",cutpoint[0]);for (int i=1;i<cutpoint.size();i++) printf(" %d",cutpoint[i]);puts("");}for(int i=0;i<cutedge.size();i++) printf("%d %d\n",cutedge[i].first,cutedge[i].second);}


0 0
原创粉丝点击