算法设计与分析 实验五图论-桥

一、实验目的:

(1)掌握图的连通性。
(2)掌握并查集的基本原理和应用。

二、问题描述:

  1. 桥的定义
    在图论中,一条边被称为“桥”代表这条边一旦被删除,这张图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。
    在这里插入图片描述

图 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.997ms5.984msTLE
并查集+LCA<1μs<1μs356.058ms
608

在这里插入图片描述
图 小规模
在这里插入图片描述
图 大图

2.稀疏图

点数n边数m压缩路径(ms)没有压缩路径(ms)
10000004000000344.772000378.453
20000004000000354.858000383.825
30000004000000370.386000381.616
40000004000000378.381000404.372

3.稠密图
m = nn0.48

点数n边数m压缩路径(ms)没有压缩路径(ms)
10004800008.97912.686
2000192000048.9549.779
30004320000126.659117.151
40007680000226.607229.36

五、实验结论:

(1)邻接表和邻接矩阵的选择:
1.稀疏图-邻接表,稠密图-邻接矩阵
2.数据量较大时,选择邻接表,因为邻接矩阵需要的空间大

(2)建立生成树
1.选择dfs,数据量大时容易发生栈溢出

(3)递归层数多,可以考虑将递归改为递推
递归需要不断调用函数,不断进行出栈和入栈的操作

代码与数据
建议自己写,不要直接CV
https://download.csdn.net/download/weixin_50325452/85533171

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值