圆方树
圆方树
前置知识: 点双
引入
通常来说, 树具有十分优良的性质, 可以通过许多数据结构来维护它.
但是对于一般的图则不具备一些性质, 所以我们考虑将一般图放在树上进行考虑.
如标题所见, 圆方树便是一种将图转化成树的方法.
定义
圆方树最初是用于处理「仙人掌图」(即每一条边在不超过一个简单环) 的一种工具, 通过发掘一些性质, 使得有时我们可以在无向图上使用.
点双
先介绍一下点双连通图: 在一个无向图中, 任意两不同点之间至少有两条点不重复的路径. 形式化一点, 即两条路径的交集为空 (不考虑起点, 终点).
这里有一个几乎等价的定义: 不存在割点的图. 但是这个定义在只有两个点, 一条边连接是会失效.
虽然原始定义是前者, 为了方便, 我们规定点双图的定义依然采用后者.
而一个图的点双联通分量则是一个极大联通子图. 与强连通分量不同, 一个点可能属于多个点双, 而一条边恰好属于一个点双.
圆方树
在圆方树中, 原来的每一个点对应圆点, 每一个点双对应方点. 一共 \(n + c\) 个点, 其中 \(c\) 为原图点双连通分量个数.
而对于每一个点双连通分量, 其对应的方点向这个点双连通分量中的每个点连边.
这样, 每一个点双形成一个「菊花图」, 多个「菊花图」通过原图中的割点连接在一起.
圆方树的点数小于 \(2n\), 因为割点的数量小于 \(n\), 所以保险起见, 所有数组需要开两倍大小.
其实, 如果原图连通, 则「圆方树」才是一棵树, 若原图有 \(k\) 个连通分量, 则其圆方树便会形成有 \(k\) 棵树的森林.
构建
这里我们只考虑连通图, 因为如果原图不连通, 那么就可以分成若干个连通图进行操作.
因为圆方树是基于点双连通分量的, 而点双连通分量又基于割点, 所以采用类似求割点的方法即可.
求割点, 我们通常使用 Tarjan
算法, 这里我们采用一种类似 Tarjan
的算法.
对原图进行 DFS
, 并且中间用到了两个关键数组 dfn
和 low
(类似于 Tarjan
).
\(\rm{dfn_u}\) 保存节点 \(u\) 的 DFS
序, \(\rm{low_u}\) 保存节点 \(u\) 的 DFS
树中的某个点 \(v\) 通过最多一次返祖边或向父亲的树边能访问到的点的最小 DFS
序.
接下来我们考虑点双和 DFS
树以及这两个数组之间的关联.
不难发现, 每个点双在 DFS
树上是一颗连通子树, 并且至少包含两个点; 特别地, 最顶端节点仅往下接一个点.
同时我们发现每条树边恰好在一个点双内.
考虑一个点双在 DFS
树中的最顶端节点 \(u\), 在 \(u\) 处确定这个点双, 因为 \(u\) 的子树包含了整个点双的信息.
由于至少有两个点, 考虑这个点双下一个点 \(v\), 则 \(u, v\) 之间存在一条树边.
不难发现, 此时一定有 \(\rm{low_v = dfn_u}\). 更准确一点, 对于一条树边 \(u \to v\), \(u, v\) 在同一点双中, 且 \(u\) 是这个点双中深度最浅的节点当且仅当 \(\rm{low_v = dfn_u}\).
这样我们可以在 DFS
的过程中确定哪些地方存在点双, 但是不能准确地确定一个点双所包含的点集.
同样类似于 Tarjan
我们用一个栈维护即可. 同时处理被弹出的节点, 只要将其和新建的方点连边即可. 不要忘记最后让 \(u\) 与方点连边.
这样便完成了圆方树的构建.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现