算法和数据结构——高等图算法(关节点)

Articulation Point(关节点)

在连通图G中,如果删除顶点u及以顶点u出发的所有边后所得的子图不连通,我们就称顶点u为图G的关节点或者连接点。

上图中灰色的顶点就是图的关节点。

输入 |V||E|
     s0 t0
     ...
     sn-1 tn-1
输出 按照升序依次输出图G的关节点
限制 1≤|V|≤100000、1≤|E|≤100000、G连通、不存在多重边、不存在自身循环

 题解:我们可以检查图在单独删除各顶点之后的连通性,但这个算法要对每一个顶点执行一次深度优先搜索(DFS),效率实在不高

我们可以将深度优先搜索加以应用,就可以有效地找出图中的所有关节点。我们需要求得一下变量:

prenum[u];//顶点u的访问顺序
parent[u];//顶点u的父亲结点
lowest[u];//与顶点u相连的顶点的最小值顺序

有了这些变量我们便可以通过下述方法确定关节点:

  • T的根结点r拥有2个以上子结点时(充分必要条件),r为关节点
  • 对于各顶点u,设u的父节点parent[u]为p。如果prenum[p]≤lowest[u](充分必要条件),则p为关节点(p为根结点时则套用1)。这表明顶点u及u在T中的子孙均不具备与p的祖先相连的边。

下面举个例子,如图

case1.注意顶点5(父结点为顶点3):由于满足prenum[3]≤lowest[5](4≤6),因此顶点3为关节点。这表明顶点5及其在T中所有子孙都不具备与顶点3的祖先相连的边。

case2.注意顶点2(父结点为顶点1):由于不满足prenum[1]≤lowest[2],因此顶点1不是关节点。这表明顶点2或其在T中的某个子孙具备与顶点1的祖先相连的边。

#include<iostream>
#include<vector>
#include<set>
using namespace std;
#define MAX 100000

vector<int>G[MAX];
int N;
bool visited[MAX];
int prenum[MAX],parent[MAX],lowest[MAX],timer;

void dfs(int current,int prev){
	//访问结点current之后立刻执行的处理
	//prenum[i]结点i的访问顺序
	//lowest[i]与结点i相连的下一个最小顺序结点 
	prenum[current]=lowest[current]=timer;
	timer++;
	
	visited[current]=true;
	
	int next;
	for(int i=0;i<G[current].size();i++){
		//与current结点相连的下一个节点 
		next=G[current][i];
		if(!visited[next]){
			//即将通过结点current访问结点next时执行的处理
			parent[next]=current; 
			
			dfs(next,current);
			//结点next搜索完毕之后立刻处理
			//若是没有访问的,则在current与next取最小顺序的下一个节点 
			lowest[current]=min(lowest[current],lowest[next]); 
		}
		//若下一个结点已被访问,且该结点不是前一个结点		
		else if(next!=prev){
			//边current-->next为Back-edge时的处理
			lowest[current]=min(lowest[current],prenum[next]); 
		}
	} 
} 
void art_points(){
	for(int i=0;i<N;i++){
		visited[i]=false;
	}
	timer=1;
	//计算lowest
	dfs(0,-1);//0 == root
	
	set<int>ap;
	int np=0;
	
	for(int i=1;i<N;i++){
		//节点i的父亲结点 
		int p=parent[i];
		//记录父结点的子结点数 
		if(p==0)
			np++;
		//如果要去点p,p的访问顺序在结点i的访问的下一个结点顺序之前时 
		else if(prenum[p]<=lowest[i])
			ap.insert(p);
	}
	if(np>1)//父结点的子结点数为两个以上是则也是一个关节点 
		ap.insert(0);
	for(set<int>::iterator it = ap.begin();it!=ap.end();it++){
		cout<<*it<<endl;
	} 
} 
int main(){
	int m;
	cin>>N>>m;
	for(int i=0;i<m;i++){
		int s,t;
		cin>>s>>t;
		G[s].push_back(t);
		G[t].push_back(s);
	}
	art_points();
	return 0;
} 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值