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

目录

一、实验目的

二、问题描述

三、实验要求

四、实验内容

(一)基准算法

(二)高效算法

五、实验结论


一、实验目的

        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.tpcf.cn/diannao/42434.html

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

相关文章

基于Android Studio订餐管理项目

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 能够实现登录&#xff0c;注册、首页、订餐、购物车&#xff0c;我的。 用户注册后&#xff0c;登陆客户端即可完成订餐、浏览菜谱等功能&#xff0c;点餐&#xff0c;加入购物车&#xff0c;结算&#xff0c;以及删减…

【学习笔记】操作系统--万字长文

计算机操作系统 文章目录 计算机操作系统引言 操作系统基本概念第一章 引论目标和作用操作系统发展历程单道批处理系统多道批处理系统分时系统实时系统 基本特征并发共享虚拟异步性&#xff08;不确定性&#xff09; 操作系统主要功能处理机管理内存管理设备管理文件管理 第二章…

python `queue` 模块提供了同步的、线程安全的队列类

在Python中&#xff0c;queue 模块提供了同步的、线程安全的队列类&#xff0c;这使得在多线程环境下共享数据变得简单。下面是一个使用 queue.Queue 的并发编程示例&#xff0c;其中使用了 threading 模块来创建多个线程&#xff0c;这些线程将向队列中添加元素并从队列中取出…

探索 WebKit 的前沿之旅:HTML5 新特性的卓越处理

探索 WebKit 的前沿之旅&#xff1a;HTML5 新特性的卓越处理 随着 Web 技术的飞速发展&#xff0c;HTML5 已经成为构建现代网页和应用的基石。WebKit&#xff0c;作为领先的浏览器引擎之一&#xff0c;承载着将这些创新技术转化为用户可感知体验的使命。本文将深入探讨 WebKit…

工程化:Commitlint / 规范化Git提交消息格式

一、理解Commitlint Commitlint是一个用于规范化Git提交消息格式的工具。它基于Node.js&#xff0c;通过一系列的规则来检查Git提交信息的格式&#xff0c;确保它们遵循预定义的标准。 1.1、Commitlint的核心功能 代码规则检查&#xff1a;Commitlint基于代码规则进行检查&a…

汇聚荣拼多多电商的技巧有哪些?

在电商平台上&#xff0c;汇聚荣拼多多以其独特的商业模式和创新的营销策略吸引了大量消费者。那么&#xff0c;如何在这样一个竞争激烈的平台上脱颖而出&#xff0c;成为销售佼佼者呢?本文将深入探讨汇聚荣拼多多电商的成功技巧。 一、精准定位目标客户群体 首先&#xff0c;…

Python魔法函数(Magic Methods简介

在 Python 中&#xff0c;魔法函数&#xff08;Magic Methods&#xff09;也称为双下划线方法&#xff08;Dunder Methods&#xff09;&#xff0c;是指那些名字以双下划线开头和结尾的特殊方法。 这些方法可以让您的自定义类实现一些特定的行为&#xff0c;从而与 Python 的内…

绝区肆--2024 年AI安全状况

前言 随着人工智能系统变得越来越强大和普及&#xff0c;与之相关的安全问题也越来越多。让我们来看看 2024 年人工智能安全的现状——评估威胁、分析漏洞、审查有前景的防御策略&#xff0c;并推测这一关键领域的未来可能如何。 主要的人工智能安全威胁 人工智能系统和应用程…

Qt 绘图详解

文章目录 头文件和构造函数启用反锯齿功能绘制矩形绘制圆角矩形绘制椭圆绘制圆弧绘制弦绘制凸多边形绘制图片绘制直线绘制多条直线绘制多点连接的线绘制路径绘制扇形绘制点绘制文本擦除矩形区域填充矩形填充路径 头文件和构造函数 #include "mainwindow.h" #include…

C-11 三角剖分的调研

C-11 三角剖分算法 三角剖分就是将输入的多边形&#xff0c;分割成一系列互不重叠的三角形&#xff0c;其重要性就在这不多赘述。这个是一个别人总结的链接&#xff1a;http://vterrain.org/Implementation/Libs/triangulate.html 图片链接&#xff1a;http://www-cgrl.cs.m…

基于CentOS Stream 9平台搭建MinIO以及开机自启

1. 官网 https://min.io/download?licenseagpl&platformlinux 1.1 下载二进制包 指定目录下载 cd /opt/coisini/ wget https://dl.min.io/server/minio/release/linux-amd64/minio1.2 文件赋权 chmod x /opt/coisini/minio1.3 创建Minio存储数据目录&#xff1a; mkdi…

springboot校园安全通事件报告小程序-计算机毕业设计源码02445

Springboot 校园安全通事件报告小程序系统 摘 要 随着中国经济的飞速增长&#xff0c;消费者的智能化水平不断提高&#xff0c;许多智能手机和相关的软件正在得到更多的关注和支持。其中&#xff0c;校园安全通事件报告小程序系统更是深得消费者的喜爱&#xff0c;它的出现极大…

关于隐藏、覆盖(重写)、重载的理解

定义区分 在派生-对象中&#xff1a;优先考虑隐藏&#xff0c;此时派生类中的覆盖(重写)也是隐藏;没有隐藏的情况下&#xff0c;子类对象才能调用父类重载函数。[此时感觉virtual没用&#xff0c;]在派生-指针或者引用中&#xff1a;只用覆盖(重写)和重载; 注&#xff1a;C Pr…

《Programming from the Ground Up》阅读笔记:p19-p48

《Programming from the Ground Up》学习第2天&#xff0c;p19-p48总结&#xff0c;总计30页。 一、技术总结 1.object file p20, An object file is code that is in the machine’s language, but has not been completely put together。 之前在很多地方都看到object fi…

高阶K8S面试题你会几个?

前言 K8S架构、公有云、持久化存储、HELM、CICD、负载均衡、监控告警、可观察性、服务治理、架构探索。。。 Q1&#xff1a;如何调试 Kubernetes 集群中的网络连接问题&#xff0c;比如 Pod 间通信失败的情况&#xff1f; 状态检查&#xff1a;使用 kubectl get pods 和 kube…

MySQL-17-mysql alter 语句如何实现?如何合并为一个

拓展阅读 MySQL 00 View MySQL 01 Ruler mysql 日常开发规范 MySQL 02 truncate table 与 delete 清空表的区别和坑 MySQL 03 Expression 1 of ORDER BY clause is not in SELECT list,references column MySQL 04 EMOJI 表情与 UTF8MB4 的故事 MySQL 05 MySQL入门教程&a…

Git使用中遇到的问题(随时更新)

问题1.先创建本地库&#xff0c;后拉取远程仓库时上传失败的问题怎么解决&#xff1f; 操作主要步骤&#xff1a; step1 设置远程仓库地址: $ git remote add origin gitgitee.com:yourAccount/reponamexxx.git step2 推送到远程仓库: $ git push -u origin "master&qu…

线程池理解及7个参数

定义理解 线程池其实是一种池化的技术实现&#xff0c;池化技术的核心思想就是实现资源的复用&#xff0c;避免资源的重复创建和销毁带来的性能开销。线程池可以管理一堆线程&#xff0c;让线程执行完任务之后不进行销毁&#xff0c;而是继续去处理其它线程已经提交的任务。 …

GStreamer学习5----probe数据探测

参考资料&#xff1a; gstreamer中如何使用probe&#xff08;探针&#xff09;获取帧数据_gstreamer 视频编码时获取视频关键帧信息-CSDN博客 Gstreamer中可以使用AppSink作为一个分支来查看管线中的数据&#xff0c;还可以使用probe去处理。 在GStreamer中&#xff0c;probe…

LayerNorm Plugin的使用与说明

目录 前言0. 简述1. Layernorm Plugin的使用1.1 源码下载1.2 模型下载和修改1.3 环境配置1.4 编译1.4 engine生成和执行(trtexec)1.5 enging生成和执行(C API) 2. 补充说明2.1 RTMO显存占用问题2.2 插件找不到的说明2.3 LayerNorm plugin封装的尝试2.4 layerNorm plugin核函数实…