割点:对于一个连通图,删去某些点(删去一个点即把该店和与该点相邻接的边都删去),得到的图将不再连通,而删去该点集的任意
子集该图依然连通,则其称为该图的一个割集,若该集合只有一个点组成,则称其为割点。
割边:对于一个连通图删去某些边(删去一条边仅删去该条边即可),得到的图将不再连通,而删去改边集的任意子集,该图依然连通,
则称其为该图的边割集,若该集合只有一条边组成,则称其为割边,又称桥。
算法实现:
首先对图进行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]); } }
在求割边的时候,一般还需要注意题目是否有重边,在读图是不能将重边覆盖。