一、实验目的:
(1)掌握图的连通性。
(2)掌握并查集的基本原理和应用。
二、问题描述:
- 桥的定义
在图论中,一条边被称为“桥”代表这条边一旦被删除,这张图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。
图 1 没有桥的无向连通图
图 2 这是有16个顶点和6个桥的图
(桥以红色线段标示)
三、算法设计原理:
1.基准法
1)算法流程:计算图的连通块数,遍历每一条边,将其从图中删除,然后计算图的连通块数与原图是否相同,如果不相同,则该边为桥,再将该边添加回图中。
计算图的连通块数:遍历每个点的邻接表,给其上色,颜色数即为连通块数。
如下图1所示:原图中有四种颜色,即四个连通分量
图2为删除一条边后,图中有五种颜色,即五个连通分量
图1 原图
图2 删除边后
2)伪代码
计算连通块数
SBLOCK()
for i = 0 to n:
if !st[i]:
dfs(i)
sblock++
计算桥的个数
CNTBRIDGE()
for i = 0 to m:
reset(st)
Remove(edge[i])
cnt = 0
for j = 0 to n:
if !st[i]
dfs(j)
cnt++
if cnt > sblock
ans++
add(edge[i])
2)LCA+并查集
基准法的缺陷
基准法:删除每一条边后,判断连通块数量是否增加。算法的效率慢。
因为一条边是一座桥当且仅当这条边不在任何环上。树是边数最小的无环图,向树上添加任意一条两个顶点都在树上的边,必定会形成环,所以桥一定是生成树上的边。
如下图3、4所示,向生成树中加入非树边后,一定会形成环,而环上的边一定不是桥。
图3 原图
图4 加边
LCA(最小公共祖先)
定义:一棵树中,两个节点的最近公共祖先
LCA算法流程:比较两个点的深度,深度大者移动到其前驱节点,再次比较,以此类推,直到两个点重合。
并查集
并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。
创建:每个点的前驱节点都是自己,也就是每个点都是自己的根节点。这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(n)
图5 并查集的创建
合并:遍历每条边,找其根节点,如果根节点不同,将两个元素所在的集合合并为一个集合。
如下图6中,2的根节点为2,1的根节点为1,加上一条边后,因为两个节点的根节点不同,所有选择让2的前驱指向1
图6 原图
图7 加边
图8 合并
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用下面的“查找”操作实现。
查找:找其前驱节点,直到找到根节点
图9 找到节点4的前驱节点
图10 找到节点4的根节点1
查找优化:压缩路径
压缩路径过程:让其前驱节点直接指向其根节点
图11 普通查找
图12 路径压缩
(1)算法流程:通过bfs生成树,遍历所有非生成 树的边,求边的两个顶点的LCA(最小公共祖先),把形成环的生成树边记录为非桥。
(2)伪代码
通过bfs计算连通块的个数
SBLOCK()
for i = 0 to n:
if !st[i]:
bfs(i)
sblock++
LCA+并查集
LCA(a, b)
if tree_pre[a] == b || tree_pre[b] == a
return
sonA = a, sonB = b
while a != b:
if depth[a] < depth[b]
st[b] = false
b = tree_pre[b]
elif depth[a] > depth[b]
st[a] = false
else
st[a] = false
st[b] = false
a = tree_pre[a]
b = tree_pre[b]
// 压缩路径
for i = sonA to i !=a:
temp = tree_pre[i]
tree_pre[i] = a
i = temp
for i = sonB to i != b:
temp = tree_pre[i]
tree_pre[i] = b
i = temp
(3)时间复杂度为O(m+n)
四、实验结果分析:
1.附件数据
小规模 | 中图 | 大图 | |
---|---|---|---|
基准法 | 0.997ms | 5.984ms | TLE |
并查集+LCA | <1μs | <1μs | 356.058ms |
桥 | 6 | 0 | 8 |
图 小规模
图 大图
2.稀疏图
点数n | 边数m | 压缩路径(ms) | 没有压缩路径(ms) |
---|---|---|---|
1000000 | 4000000 | 344.772000 | 378.453 |
2000000 | 4000000 | 354.858000 | 383.825 |
3000000 | 4000000 | 370.386000 | 381.616 |
4000000 | 4000000 | 378.381000 | 404.372 |
3.稠密图
m = nn0.48
点数n | 边数m | 压缩路径(ms) | 没有压缩路径(ms) |
---|---|---|---|
1000 | 480000 | 8.979 | 12.686 |
2000 | 1920000 | 48.95 | 49.779 |
3000 | 4320000 | 126.659 | 117.151 |
4000 | 7680000 | 226.607 | 229.36 |
五、实验结论:
(1)邻接表和邻接矩阵的选择:
1.稀疏图-邻接表,稠密图-邻接矩阵
2.数据量较大时,选择邻接表,因为邻接矩阵需要的空间大
(2)建立生成树
1.选择dfs,数据量大时容易发生栈溢出
(3)递归层数多,可以考虑将递归改为递推
递归需要不断调用函数,不断进行出栈和入栈的操作文章来源:https://www.toymoban.com/news/detail-464209.html
代码与数据
建议自己写,不要直接CV
https://download.csdn.net/download/weixin_50325452/85533171文章来源地址https://www.toymoban.com/news/detail-464209.html
到了这里,关于算法设计与分析 实验五图论-桥的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!