图的割点/割边

割点:对于一个连通图,删去某些点(删去一个点即把该店和与该点相邻接的边都删去),得到的图将不再连通,而删去该点集的任意

子集该图依然连通,则其称为该图的一个割集,若该集合只有一个点组成,则称其为割点。

割边:对于一个连通图删去某些边(删去一条边仅删去该条边即可),得到的图将不再连通,而删去改边集的任意子集,该图依然连通,

则称其为该图的边割集,若该集合只有一条边组成,则称其为割边,又称桥。

算法实现:

首先对图进行dfs,主要借助两个数组来完成工作。

①: dfn(u)为结点u在搜索树中的次序号;

②: 定义low(u)为结点u或结点u的子孙中能通过非父子边追溯到的dfn最小的结点。

low的求法如下:

low(u) = dfn(u);
for(每个u能到达的结点v){
	if(v不是u的父结点 && dfn(v) < dfn(u))
		low(u) = min(low(u), dfn(v));
	if(u是v的父结点)
		low(u) = min(low(u), low(v)); 
}

    如果u是根结点,且u有多于一颗子树,则u是割点。若u不是根结点,且存在u的孩子v,使得dfn(u) <= low(v),也就是说去掉结点u后,

v并不能到达u的祖先,则u是割点。

    u是v的父结点,若满足dfn(u) < low(v),即去掉边(u, v)以后v无法再到达u了,则为割边(这里如果dfn(u) >= low(v),则说明v还有其他

方法可以到达u,因此不是割边)。

    下面是求割点和割边的参考代码。其中,father为当前结点的父结点,dep记录当前深度,初始化为0。dfn初始化为-1,每个点被访问时都

会将dfn置为当前dep。low数组随着dfs而更新。

void Tarjan(int u, int father)
{
	dfn[u] = low[u] = dep++;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to;
		if(dfn[v] == -1){
			Tarjan(v, u);
			low[u] = min(low[u], low[v]);
			if(u == root)
				cnt++;		//cnt记录root有多少子树 
			else if(low[v] >= dfn[u])	//u是割点,这里改为low[v] > dfn[u],则(u,v)是一条割边 
		}
		else if(v != father)
			low[u] = min(low[u], dfn[v]); 
	}	
}

  在求割边的时候,一般还需要注意题目是否有重边,在读图是不能将重边覆盖。

       

          

你可能感兴趣的:(图论)