【图论复习】Tarjan 算法(Tarjan LCA 除外)

好久没写 Tarjan,反正也快 CSP 了,赶紧复习一下。
Tarjan 就是基于 dfs 树中的 dfs 序 以及 low 数组来进行搜索,注意不同的算法 low 的更新时不一样的,其他的感觉没什么好讲的,基本上可以说是背代码的吧。
复杂度都是 Θ(n+m)

强连通分量

对于一个有向图 G=(V,E),存在点集一个的子集,使得这个子集中任意两个点都可以互相到达,那么这个子集被称为这张图的连联通分量。联通分量的极大值被称为强联通分量。

Tips:
注意需要另外开一个 vis 数组代表这个节点是否在栈内,如果在栈内才能更新 low。

void dfs(int x){
dfn[x]=low[x]=++cnt; vis[x]=1; st[++top]=x; int i;
for(i=head[x];i;i=nex[i]){
if(!dfn[to[i]]) dfs(to[i]);
if(vis[to[i]]) low[x]=mmin(low[x],low[to[i]]);
}
if(low[x]==dfn[x]){
num++; scc[x]=num; sum[num]=c[x]; vis[x]=0;
while(st[top]!=x) scc[st[top]]=num,vis[st[top]]=0,sum[num]+=c[st[top]],top--; top--;
} return;
}

割点

对于一个无向图,如果一个点删除之后,这张图其他点的连通性改变,那么这个点被称之为割点。

Tips:
如果接下来的节点之前访问过,那么只能用下一个节点的 dfn 更新当前点的 low 而非是当前下一个点的 low。
注意判断是不是割点是 low[to[i]]>=dfn[x] (带等号),而且需要特判根。

void dfs(int x,int fa){
dfn[x]=low[x]=++cnt; int i,d=0;
for(i=head[x];i;i=nex[i]) if(!dfn[to[i]]){
d++; dfs(to[i],fa); low[x]=mmin(low[x],low[to[i]]);
if(low[to[i]]>=dfn[x]&&x!=fa) flag[x]=1;
} else low[x]=mmin(low[x],dfn[to[i]]);
if(x==fa&&d>1) flag[x]=1; return;
}

割边

对于一个无向图,如果一条边删除之后,这张图其他点的连通性改变,那么这个边被称之为割边,又称作桥。

Tips:
更新 low 和割点一样。
注意判断是不是割边是 low[to[i]]>dfn[x] (不带等号)。flag[x] 代表 x 和 fa[x] 之间的边是不是割边。
注意重边。

void dfs(int x,int pre){
dfn[x]=low[x]=++cnt; int i,d=0;
for(i=head[x];i;i=nex[i]) if(!dfn[to[i]]){
d++; fa[to[i]]=x; dfs(to[i],i); low[x]=mmin(low[x],low[to[i]]);
if(low[to[i]]>dfn[x]) flag[x]=1;
} else if(i!=(pre^1)) low[x]=mmin(low[x],dfn[to[i]]);
return;
}

点双

对于一个无向图,如果一个极大子图没有一个点是割点,那么这个子图被称为点双连通分量。

Tips:
更新 low 和割点一样。
加入点双条件和割点一样(带等于)。
注意退栈的时候是退到 to[i](要退),当前节点不需要退栈但是需要加入点双(所以存在一些点在多个点双内)

void dfs(int x,int fa){
dfn[x]=low[x]=++cnt; st[++top]=x; int i,d=0;
for(i=head[x];i;i=nex[i]) if(!dfn[to[i]]){
d++; dfs(to[i],x); low[x]=mmin(low[x],low[to[i]]);
if(low[to[i]]>=dfn[x]){
num++; ans[num].push_back(x);
while(st[top+1]!=to[i]) ans[num].push_back(st[top]),top--;
}
} else if(to[i]!=fa) low[x]=mmin(low[x],dfn[to[i]]);
if(fa==0&&d==0) ans[++num].push_back(x); return;
}

边双

对于一个无向图,如果一个极大子图没有一个边是割边,那么这个子图被称为边双连通分量。

Tips:
注意更新 low 和割点一样。
注意加入边双的条件和强连通分量一样。

void dfs(int x,int pre){
dfn[x]=low[x]=++cnt; st[++top]=x; int i,d=0;
for(i=head[x];i;i=nex[i]) if(!dfn[to[i]]){
d++; dfs(to[i],i); low[x]=mmin(low[x],low[to[i]]);
} else if(i!=(pre^1)) low[x]=mmin(low[x],dfn[to[i]]);
if(dfn[x]==low[x]){
++num;
while(st[top]!=x) ans[num].push_back(st[top]),top--;
ans[num].push_back(st[top]),top--;
} return;
}
posted @   jiangtaizhe001  阅读(62)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示