【图论复习】Tarjan 算法(Tarjan LCA 除外)
好久没写 Tarjan,反正也快 CSP 了,赶紧复习一下。
Tarjan 就是基于 dfs 树中的 dfs 序 以及 low 数组来进行搜索,注意不同的算法 low 的更新时不一样的,其他的感觉没什么好讲的,基本上可以说是背代码的吧。
复杂度都是 。
强连通分量
对于一个有向图 ,存在点集一个的子集,使得这个子集中任意两个点都可以互相到达,那么这个子集被称为这张图的连联通分量。联通分量的极大值被称为强联通分量。
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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析