算法设计与分析 实验5 并查集法求图论桥问题

news2024/11/13 9:40:05

目录

一、实验目的

二、问题描述

三、实验要求

四、实验内容

(一)基准算法

(二)高效算法

五、实验结论


一、实验目的

        1. 掌握图的连通性。

        2. 掌握并查集的基本原理和应用。

二、问题描述

        在图论中,一条边被称为“桥”代表这条边一旦被删除,这个图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上,一个图可以有零或多座桥。现要找出一个无向图中所有的桥,基准算法为:对于图中每条边uv,删除该边后,运用BFS或DFS确定u和v是否仍然连通,若不连通,则uv是桥。应用并查集设计一个比基准算法更高效的算法,不要使用Tarjan算法。

                                             

                    图1 没有桥的无向连通图              图2 有16个顶点和6个桥的图(桥以红色线段标示)

三、实验要求

        1. 实现上述基准算法。

        2. 设计的高效算法中必须使用并查集,如有需要,可以配合使用其他任何数据结构。

        3. 用图2的例子验证算法的正确性,该图存储在smallG.txt中,文件中第1行是顶点数,第2行是边数,后面是每条边的两个端点。

        4. 使用文件mediumG.txt和largeG.txt中的无向图测试基准算法和高效算法的性能,记录两个算法的运行时间。

        5. 实验课检查内容:对于smallG.txt、mediumG.txt、largeG.txt中的无向图,测试高效算法的输出结果和运行时间,检查该代码,限用C或C++语言编写。其中smallG.txt和mediumG.txt为必做内容,运行时间在4分钟内有效,直接在终端输出结果和运行时间。以smallG.txt为例,输出如下:

6

0 1

2 3

2 6

6 7

9 10

12 13

0.002

        其中,第一行的6表示桥数,接下来的6行分别是6座桥的两个端点,小端点在前,大端点在后,6座桥按照端点从小到大的顺序输出,最后一行的0.002为整个main函数的运行时间,单位为秒。

四、实验内容

(一)基准算法

1. 算法描述:

        根据桥的定义,我们可以知道,一条边(𝑢, 𝑣)是桥,当且仅当删除边(𝒖, 𝒗)后,图的连通块数量会增加。我们可以枚举整个边集,并依次检查在删除每条边后连通块数量是否增加。具体我们可以用以下方式实现基准算法:

                使用深度优先搜索算法𝑑𝑓𝑠获取整张图的连通块个数𝑁。

                遍历整张图边集,对于每条边(𝑢, 𝑣),首先在图中将其删除。

                再次使用深度优先搜索算法𝑑𝑓𝑠获取整张图的连通块个数𝑁′。

                若𝑁′>𝑁,则(𝑢, 𝑣)为桥,否则(𝑢, 𝑣)不为桥。

                将边(𝑢, 𝑣)恢复。回到步骤 2,直至遍历完全部边集。

2、伪代码

3. 复杂度分析

        (1)时间复杂度:不妨设图中点的数量为𝑛,边的个数为𝑚。使用𝐷𝐹𝑆的时间复杂度为

𝑂(𝑛 + 𝑚)。又因为一共有𝑚条边需要进行判断,所以一共要做𝑚次𝐷𝐹𝑆,总时间复杂度为𝑂(𝑚𝑛 + 𝑚2)。

                对于稀疏图,因为有𝑚 ∝ 𝑛,所以该算法时间复杂度可表示为𝑂(𝑛2);

                对于稠密图,因为有𝑚 ∝ 𝑛2,所以该算法时间复杂度可表示为𝑂(𝑛4)。

       (2)空间复杂度:我们用邻接表对整张图进行储存,空间复杂度一共为𝑂(𝑛 + 𝑚)。

4. 数据测试分析

              我们分别取点的数量𝑛 = 1000、2000、3000、4000 和 5000,边的数量𝑚 = 𝑛作为稀疏,边的数量𝑚 =n*(n-1)/2作为稠密图对算法进行测试。

              为降低偶然性,我们对每组数据重复测试 10 次再取平均值

1.1 稀疏图基准算法运行时间表

点的数量𝑛

1000

2000

3000

4000

5000

实际运行时间(ms)

16.5215

77.4551

197.404

397.095

650.526

理论运行时间(ms)

16.5215

77.32

196.641

388.781

638.223

1.2 稠密图基准算法运行时间表

点的数量𝑛

100

200

300

400

500

实际运行时间(ms)

219.495

4039.62

22930.6

67342

190571

理论运行时间(ms)

219.495

3511.92

19779.095

62190.72

170184.375

        从上表和上图可以看出,无论是在稀疏图还是在稠密图中,算法实际运行的时间和理论运行时间曲线拟合效果良好

5. 基准算法优化

       一次删边操作影响的只是其中一个连通分量,不需要对全图进行𝐷𝐹𝑆 。所以我们在删除边之后,只对所删除边的其中一个端点做𝑫𝑭𝑺 :

如果在深度优先搜索过程中搜索到另一结点,则说明边的两个端点在同一连通分支,该边不是桥。

若从一个端点出发没有搜到另一个端点,则说明边的两个端点不在在同一连通分支,该边是桥。


伪代码描述:

耗时对比:

2.1 稀疏图中基准算法及优化基准算法运行时间

点的数量𝑛

1000

2000

3000

4000

5000

未优化(ms)

15.275

84.8875

222.129

433.341

720.145

判断优化(ms)

9.03036

39.3281

88.6593

167.861

267.835

2.2 稠密图中基准算法及优化基准算法运行时间

点的数量𝑛

100

200

300

400

500

未优化(ms)

219.495

4039.62

22930.6

67342

190571

判断优化(ms)

16.631

250.045

1301.25

4629.83

10228.2

        在进行判断优化的基础上,在稀疏图中,代码运行效率提升了50%,且点数越多,优化效果越明显;在稠密图中,提升效果则更加明显

(二)高效算法

1. 数据结构介绍

并查集

        并查集是一种树型的数据结构,可以解决局部联通问题。并查集把有联系的元素放入同一个集合,用集合中的一个“有代表性”的元素来表示整个集合,其中集合内部的关系是用一个𝑓𝑎指针来维护。并查集常见的操作有查询与合并这两种操作。

        查询操作:查询即查询当前两元素是否在同一集合中。我们可以通过递归层层向上访问,直至访问到根节点。若两元素的根节点相同,则说明两元素在同一集合中。否则不在同一集合中。

        合并操作:合并即将不在同一集合中的两个子集合合并为一个集合。对于两个需要合并的元 素,我们首先通过查询得出两个集合的根节点,然后修改其中一个根节点的𝑓𝑎指针,使其指向另一集合的根节点即可

路径压缩

        随着集合元素变多,链越来越长,我们想要从底部找到根节点就需要经过层层递 归,这使得  操作变得越来越难。既然我们只关心一个元素对应的根节点,那么我们就考虑将集合中每个元素的𝑓𝑎指针直接指向根节点。

        实现方式就是在查询的时候,把沿途的每个节点的父节点都设置为根节点,这样就可以的降低查询时递归向上的查询深度

STL

       向量vector,一个大小可动态变化的顺序数组容器

       vector 容器一方面可以动态的分配空间,比一般的数组更合理使用空间;另外它有很多基本函数,如push_back()函数类似队列中的push()函数,size()可以直接返回当前容器长度。因为 vector 可以更方便的使用函数调用和管理空间,高效算法构建邻接表就通过 vector 实现。

对于大数据量级下的图结构,如果使用临近矩阵进行存储,会造成内存溢出,因此也必须使用邻接表进行存储。

2. LCA算法-引入最近公共祖先

       在一棵没有环的树上,除根节点外每个节点都有其父节点和祖先节点,最近公共祖先就是两个节点在这棵树上深度最大的公共祖先节点。

       LCA(Least Common Ancestors),用于求解两个节点的最近公共祖先的算法。其可用于最短路径计算,连通性判断,图论问题等。

如上图,4和6的最近公共祖先为5

       在本实验中,我们可以利用最近公共祖先(LCA)算法找出生成森林中的环边,即把找LCA 时经过的边删除,生成森林中剩余的边即为桥。

3. 算法思想

       根据桥的定义和图论的相关知识,我们可以知道,一条边是桥当且仅当这条边不在任何环上才成立。那么我们可以用排除法得到图中的桥,即用 “总边 - 环边”。

         又由图论知识可以得出,树是边数最大的无环图,若向树上添加任意一条边时,必然会形成环,所以我们只需找出生成树中的环边,除去这些即为桥。

       基于这一想法,可以先构建生成树

我们接着枚举不在生成森林上的所有边,然后可以利用最近公共祖先(LCA)算法找出生成森林中的环边,即把找LCA 时经过的边删除,生成森林中剩余的边即为桥。

       对于下图来说,蓝色的边是非生成森林中的边,我们对该边左右两个节点求LCA:首先比较两个节点的深度,将深度大的节点跳转到其父节点,并删除跳转时经过的边(图中显示为红色),重复这个过程直到两个点跳转到同一个点,即跳转到它们的 LCA。可以看到,红颜色的边就是利用LCA 算法找出的生成森林中的环边。但由于有些树的路径繁长,可通过并查集进行路径压缩提高搜索效率

4. 算法实现

并查集-朴素并查集实现求图的桥算法实现

查询操作和路径压缩

合并操作  算法优化

        合并操作算法优化—按秩合并,通过size向量,记录每个集合的大小,并在合并时选择将较小的集合合并到较大的集合上,从而实现有效控制树的高度,提高并查集的查找效率

LCA算法实现

       并查集+LCA算法求解桥问题的算法实现,首先对整个图分为不同连通块进行深度优先搜索,随后通过LCA寻找最近公共祖先,对形成环的边进行分析处理,最后剩下边的数量即为桥的数量。

       LCA求解最近公共祖先

路径压缩

5. 复杂度分析

        (1)时间复杂度:最坏情况下当生成树是一条链的时候,此时的时间复杂度为𝑂(𝑛),故总时间复杂度为𝑂(𝑚𝑛)

        对于稀疏图,因为有𝑚 ∝ 𝑛,所以该算法时间复杂度可表示为𝑂(𝑛2);对于稠密图,因为有𝑚 ∝ 𝑛2,所以该算法时间复杂度可表示为𝑂(𝑛3)。

       对于大数据量级下的查找操作,经过并查集的路径压缩,很快需要查找的节点基本上父节点大部分都已经被设置为最近公共祖先(LCA)。此时,查找的时间会接近O ( 1 ) ,则最终的时间复杂度将接近O(m)

        (2)空间复杂度:我们用邻接表对整张图进行储存,空间复杂度一共为𝑂(𝑛 + 𝑚)。

6. 数据测试分析

        我们分别取点的数量𝑛 = 1000、2000、3000、4000 和 5000,边的数量𝑚 = 𝑛作为稀疏,边的数量𝑚 =n*(n-1)/2作为稠密图对算法进行测试。

        为降低偶然性,我们对每组数据重复测试 10 次再取平均值

2.1 稀疏图中高效算法运行时间

点的数量𝑛

1000

2000

3000

4000

5000

实际运行时间

4.0968

5.9614

7.9826

9.2091

17.4451

理论运行时间

4.0968

16.3872

36.8712

65.5488

102.42

2.2 稠密图中高效算法运行时间

点的数量𝑛

100

200

300

400

500

实际运行时间

3.7533

22.5624

62.2405

162.192

212.828

理论运行时间

3.7533

30.0264

101.339

240.211

469.163

        由于𝑂(𝑛2)是算法在稀疏图的理论上界,是在最坏的情况下才会出现的结果,所以实际运行时间曲线要低于理论运行时间曲线,即 LCA 算法在稀疏图中时间复杂度的理论上界为𝑂(𝑛2)。由于𝑂(𝑛3)是算法在稠密图的理论上界,是在生成森林为一条链时才会出现的结果,所以实际运行时间曲线要低于理论运行时间曲线,即 LCA 算法在稠密图的时间复杂度的理论上界为𝑂(𝑛2)。

三) 综合对比分析

        将结果综合分析,如下:

图中各种算法运行时间

点的数量

1000

2000

3000

4000

5000

基准算法

16.5215

77.4551

197.404

397.095

650.526

高效算法

0.43826

0.86072

1.11762

1.31792

1.71322

 稠密图中各种算法运行时间

点的数量

1000

2000

3000

4000

5000

基准算法

219.495

4039.62

22930.6

67342

190571

高效算法

0.43826

0.86072

1.11762

1.31792

1.71322

       用并查集结合LCA算法的程序运行时间无论是在稀疏图还是在稠密图,都远低于基准算法所消耗的时间

对附录中的三个图文件进行运行得出:

图文件

基准算法

高效算法

桥个数

smallG.txt

0.002s

0.00017s

6

mediumG.txt

0.124s

0.00020s

3

largeG.txt

Time-out

0.98503s

8

五、实验结论

实验总结

在本次实验中,我使用了基准算法、并查集算法结合实现了在无向图中找出所有的桥,并对上述算法提出了不同的优化方式。之后对各个算法进行多次测试与比较,在算法的运行时间上我们得出以下结果:

LCA 算法+并查集算法   <<<< 基准算法

       所以对于找出一个无向图中所有桥的问题,选好解决算法十分重要

实验思考

        在本次实验中,我使用了大量STL容器,因此在编译时添加O3优化,可以节约程序运行时间

        在使用生成森林优化的时候,最好使用 bfs 来构建生成森林。这是因为使用 dfs 构建的生成森林的深度通常会非常的深(接近一条链,也就是我们前面所说的最坏情况),从而导致求 LCA 时需要遍历的点非常的多,降低了程序的运行效率。

经过测试,使用bfs代替dfs可以替身近40%的效率

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1905941.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Three.js机器人与星系动态场景(四):封装Threejs业务组件

实际在写业务的时候不会在每个组件里都写几十行的threejs的初始化工作。我们可以 将通用的threejs的场景、相机、render、轨道控制器等进行统一初始化。同时将非主体的函数提到组件外部&#xff0c;通过import导入进组件。将业务逻辑主体更清晰一些。下面的代码是基于reactthre…

(附源码)springboot共享单车管理系统-计算机毕设 65154

springboot共享单车管理系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于共享单车管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了共享单车管理系…

Leetcode3194. 最小元素和最大元素的最小平均值

Every day a Leetcode 题目来源&#xff1a;3194. 最小元素和最大元素的最小平均值 解法1&#xff1a;排序遍历 将数组 nums 排序后&#xff0c;利用双指针计算每一对 (minElement maxElement) / 2&#xff0c;最小值即为答案。 代码&#xff1a; /** lc appleetcode.cn …

多线程网络实战之仿qq群聊的服务器和客户端

目录 一、前言 二、设计需求 1.服务器需求 2.客户端需求 三、服务端设计 1.项目准备 2.初始化网络库 3.SOCKET创建服务器套接字 4. bind 绑定套接字 5. listen监听套接字 6. accept接受客户端连接 7.建立套接字数组 8. 建立多线程与客户端通信 9. 处理线程函数&…

iptables实现端口转发ssh

iptables实现端口转发 实现使用防火墙9898端口访问内网front主机的22端口&#xff08;ssh连接&#xff09; 1. 防火墙配置(lb01) # 配置iptables # 这条命令的作用是将所有目的地为192.168.100.155且目标端口为19898的TCP数据包的目标IP地址改为10.0.0.148&#xff0c;并将目标…

【Java】垃圾回收学习笔记(一):Root Search 根可达算法+垃圾回收的起点

文章目录 1. 引用计数法优点缺点 2. 可达性分析 Root Search2.1 那些对象是GC Roots2.2 引用的分类2.3 回收方法区 3. 实现细节3.1 GC的起点&#xff1a;节点枚举OopMap&#xff1a;帮助高效的根节点枚举 3.2 何时开始GC&#xff1a;安全点与安全区域如何选取安全点如何让程序进…

数据驱动的内容优化:Kompas.ai如何提升内容表现

在数字化营销时代&#xff0c;内容是企业与用户沟通的重要桥梁。然而&#xff0c;随着信息量的爆炸性增长&#xff0c;如何让内容在激烈的竞争中脱颖而出&#xff0c;成为每个营销人员面临的问题。数据驱动的内容优化策略&#xff0c;通过精准分析和科学决策&#xff0c;帮助品…

华为OD机试 - 员工派遣(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

基于字典学习的地震数据降噪(MATLAB R2021B)

稀疏表示基于研究者们提出了许多变换基函数的方法逐渐成型&#xff0c;比如小波域&#xff0c;曲波域&#xff0c;dreamlet 域等&#xff0c;其原理是利用地震信号在变换域内的稀疏性和可分离性以去除噪声。继 Donoho发表非线性去噪方法-小波阈值萎缩方法&#xff0c;在后续的研…

Swift 中 map 和 flatMap 的区别 (入门版)

在 Swift 中&#xff0c;map 和 flatMap 是用于处理集合类型&#xff08;如数组、字典、集合等&#xff09;的两个重要方法。尽管它们看起来相似&#xff0c;但它们在处理集合中的元素和结果时有着不同的行为。 map 方法 map 方法会对集合中的每一个元素应用一个变换&#xff…

C++ 面试宝典之:空类大小究竟是不是 0?

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/pD4bIjX2kDzo8gbYRPktPQ 首先&#xff0c;空类是什么&#xff1f;空类指的是不包含任何数据成员的类&#xff0c;但可能包含方法成员。 实例化时…

Redis常用命令——Set、Zset篇

文章目录 一、Set相关命令操作 SADD SMEMBERS SISMEMBER SCARD SPOP SMOVE SREM SINTER 与 SINTERSTORE SUNION 与 SUNIONSTORE SDIFF 与 SDIFFSTORE Set命令小结 二、Zset 相关命令操作 ZADD ZCARD ZCOUNT ZRANGE ZREVRANGE ZPOPMAX BZPOPMAX ZPOPMIN 与 BZPOPMIN ZRANK 与 …

AI Earth ——开发者模式案例10:基于 CNN 的 AI 分类模型开发

基于 CNN 的 AI 分类模型开发 本案例主要介绍如何快速利用 AIE Python SDK 创建机器学习建模流程。我们主要使用到 Python SDK的Machine Learning Proxy 模块(下文简称 AieMlProxy )。该模块涵盖了一系列用户与训练集群之间的交互接口,包括:鉴权、数据加载、训练任务提交、…

OpenCV杂记(4):OpenCV之色彩映射(伪彩applyColorMap)

1. 简述 我们在开发基于热成像&#xff08;红外&#xff09;或者做深度估计应用时&#xff0c;为了便于直观的观察&#xff0c;常常将检测结果进行色彩上的映射&#xff0c;这样便可以很直观的看出哪里温度高&#xff0c;哪里温度低&#xff0c;或者哪里深度更深或更浅。 我们将…

【STM32项目】基于Stm32搞怪盒子的设计(完整工程资料)

基于stm32搞怪的盒子设计 前言&#xff1a; 最近我看到一个极具创意的搞怪盒子&#xff0c;设计得相当有意思。作为一个热衷于电子DIY的狂热爱好者&#xff0c;怎能错过这样一个有趣的项目呢&#xff1f;于是&#xff0c;我决定亲自动手&#xff0c;设计一个属于自己的、独一无…

代码随想录——单调递增的数字(Leetcode738)

题目链接 贪心 class Solution {public int monotoneIncreasingDigits(int n) {char[] digits String.valueOf(n).toCharArray();int flag digits.length;for (int i digits.length - 1; i > 0; i--) {if (digits[i] < digits[i - 1]) {flag i;digits[i - 1]--;}}…

KVM把新添加的磁盘扩容到根目录

1、对新增的磁盘进行分区&#xff08;注&#xff1a;可省略&#xff09; PS&#xff1a;使用fdisk或gdisk&#xff08;大于2T时使用&#xff09;对新增磁盘进行分区。 [rootkvm-clinet ~]# fdisk/dev/sdb Welcome to fdisk (util‐linux 2.23.2).4 Changes will remain in …

GISSERVER 管理器发布切片服务

GISSERVER 管理器 1.0(私有化地图离线部署)可以为您发布切片服务&#xff0c;切片服务的概念可以见我以前的文章&#xff1a;如何生成像谷歌高德一样的切片地图。除了我们自己制作的切片外&#xff0c;我们最常见的是利用各类地图下载器下载的在线地图的切片。见&#xff1a;栅…

Qt文档阅读笔记-Queued Custom Type Example

此篇展示了使用Qt编写多线程程序。 概述 此案例创建一Block类&#xff0c;用于存储数据&#xff0c;并且在元对象系统中注册后&#xff0c;在多线程中进行信号与槽函数的连接中充当参数。 Block类 在元对象系统中&#xff0c;注册类&#xff0c;需要类在public部分提供默认构…

56、最近邻向量量化(LVQ) 网络训练对输入向量进行分类

1、LVQ 网络训练对输入向量进行分类简介 1&#xff09;简介 LVQ&#xff08;最近邻向量量化&#xff09;是一种简单而有效的神经网络模型&#xff0c;用于对输入向量进行分类。LVQ网络通过学习一组原型向量&#xff08;也称为代码矢量或参考向量&#xff09;&#xff0c;来表…