<<<上一篇
①:无向图基本概念
②:tarjan算法求无向图割边
前言
第一次写算法,讲得肯不透彻,有误还请指教awa
先来回顾一下dfs的基本框架:
//存图方式:vector(g[N]) void dfs(int u){//u:当前节点 vis[u]=true; for(int& v:g[u]){//访问u连到的每个节点 if(!vis[v]) dfs(v); } }
请注意,这里讲的仅限于无向图,有向图的算法会稍有不同。
depth[]
和low[]
,定义如下:depth[i]
:节点i在DFS-tree上的深度。low[i]
:在dfs-tree上,以节点i及其子孙为起点的回边回到的 最低 高度。假设我们已经算出每个节点的depth
(下简称dep
)和low
,如何判断割边呢?
(务必注意,dep
和low
越大意味着越晚被遍历)
分类讨论一下吧:(假设目前在处理tree2上u->v边)
l
o
w
[
v
]
<
=
d
e
p
[
u
]
low[v]<=dep[u]
low[v]<=dep[u]:意味着v(下方点)能回到u(上)及u以上的点,此时拿掉u-v,仍有至少1条路径使v能回溯到上面,不会断开,故不是桥。
l o w [ v ] > d e p [ u ] low[v]>dep[u] low[v]>dep[u]:意味着v只能回到u以下,此时若拿掉u-v,u、v间回断开,故是桥。
(很久以前的笔记)
至此,我们已经明确割边的判断,最后一件事便是求low
值 了:
//vector<> g存图 int d=0;//深度从0开始 void dfs(int u){ vis[u]=1; dep[u]=low[u]=d++; for(int i=0;i<g[u].size();++i){ int& v=g[u][i]; if(!vis[u]){//树边 dfs(v); low[u]=min(low[u],low[v]);//更新 low[]值 //仅在是树边时判断 if(low[u]>dep[v]) printf("%d-%d\n",u,v); } else{//回边 low[u]=min(low[u],dep[v]); } } }
//求图的连通分量 int st[N],tp=0;//栈 int cn;//分量数量 int d=0; void dfs(int u){ vis[u]=1; dep[u]=low[u]=d++; st[tp++]=u;//入栈 for(int i=0;i<g[u].size();++i){ int& v=g[u][i]; if(!vis[u]){ dfs(v); low[u]=min(low[u],low[v]); if(low[u]>dep[v]){ printf("第%d个连通分量:",++cn); int t; do{ t=st[tp--]; printf("%d ",t); }while(t!=u); printf("\n"); } } else{ low[u]=min(low[u],dep[v]); } } return; } #if 0 算法实现:若u->v是桥,则当前栈出到v(v出)。 #endif
顺带一提,边-2-连通图 指至少删掉2条边才不连通(即无桥)。
证明:
因为是无向图,所以dfstree上的有向边在原图上无向,那么dfstree底图上回边形成的环在原图也是成立的,那么对于环上的任意u,v,都至少有2条路径互通,拿掉一条后至少剩一条,所以回边不是桥。
如图: ↩︎
即DFS-tree,下同。 ↩︎