算法设计与分析(实验5)-----图论—桥问题

一.实验目的

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

二.实验步骤与结果

1.定义

(1)图的相关定义

图:由顶点的有穷非空集合和顶点之间的边的集合组成。

连通图:在无向图G中,若对于任意两点x与y有路径,则称x与y连通,图G为连通图。

连通分量:非连通图的极大连通子图为连通分量。

(2)桥的定义

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

2.解决问题

        找出一个无向图中所有的桥。

要求:

(1)实现基准算法。

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

(3)用图2的例子验证算法正确性。

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

(5)设计的高效算法的运行时间作为评分标准之一。

(6)提交程序源代码。

(7)实验报告中要详细描述算法设计的思想,核心步骤,使用的数据结构。

3.实验过程

(1)基准算法

①算法原理:

For every edge (u, v), do following

a) Remove (u, v) from graph

b) See if the graph remains connected (We can either use BFS or DFS)

c) Add (u, v) back to the graph.

 

②算法伪代码:

void jizhun(int edge_i)//遍历所有的边

      n1=count();

      remove(edge_i);//删掉该边

      n2=count();

      add(edge_i);//补回刚才暂时删掉的边

      if(n1!=n2)

           return 1;//该边是桥

      return 0;//该边不是桥

③时间复杂度分析:

穷举删除的边需要e次,每次删除都要dfs判断连通分支数目,需要O(n+e),复杂度O(e)

对于稀疏图(e=n):复杂度为(n^2)

对于稠密图(e=n^2):复杂度为(n^4)

 ④小规模测试:

不同规模下算法运行效率和理论值对比

表1 稀疏图下算法效率O(n^2)

1000

2000

3000

4000

5000

实验值

0.0636

0.2768

0.6843

1.1585

1.6531

理论值

0.0636

0.2544

0.5724

1.0176

1.59

表2 稠密图下算法效率O(n^4)

100

150

200

250

300

实验值

0.6469

3.0139

8.8914

22.2807

45.062

理论值

0.6469

3.274931

10.3504

25.26953

52.3989

        实验结果表明,基准算法解决桥问题理论值和实验值在不同数据规模下几乎相同,基准算法解决稀疏图理论分析得出的O(n^2)的平均时间复杂度是相对准确的,基准算法解决稠密图理论分析得出的O(n^4)的平均时间复杂度是相对准确的。 

(2)基准法+并查集

①算法原理:

        与基准法思路相同,通过删除边并计算连通分支数目来查找桥,计算连通分支时使用并查集。并查集计算连通分支数目的步骤为:枚举边,对每个边上的两点v1和v2,查询v1和v2所属的集合f1,f2,如果v1和v2不在同一个集合则合并v1和v2所属的两个集合,最后统计集合的个数,即为连通分支数目。

②算法伪代码:

void bingchaji()

      for u in agj[v]:

             f1=find(v)

             f2=find(u)

             if f1 != f2:

                   father[f2]=f1

int find(x)

       if father[x]==x

             return x;

       father[x]=find(father[x])  //路径压缩

return father[x];

使用路径压缩策略,使得并查集的查询复杂度均摊下来为O(1)

③时间复杂度分析:(其中 n为顶点数,e为边数)

穷举删除的边需要e次,每次删除都用并查集判断连通分支数目,需要O(e),复杂度O(e)

对于稀疏图(e=n),复杂度为O(n2),对于稠密图(e=n2),复杂度为O(n4)

稀疏图

1000

2000

3000

4000

5000

实际值

0.0668

0.3459

0.6208

1.1796

1.8812

理论值

0.0668

0.2672

0.6201

1.0688

1.67

稠密图

100

150

200

250

300

实际值

1.119

4.9772

15.8986

38.1267

80.5864

理论值

1.119

5.664938

17.904

43.71094

90.639

        实验结果表明,基准+并查集算法解决桥问题理论值和实验值在不同数据规模下几乎相同,基准+并查集算法解决稀疏图理论分析得出的O(n^2)的平均时间复杂度是相对准确的,基准算法解决稠密图理论分析得出的O(n^4)的平均时间复杂度是相对准确的。 

④优化效果

稀疏图

1000

2000

3000

4000

5000

优化前

0.0636

0.2768

0.6843

1.1585

1.6531

优化后

0.0668

0.3459

0.6208

1.1796

1.8812

        分析得知,当数据规模较小时,算法的优化效果不明显,接下来在基准+并查集的算法基础上再设计优化算法。

(3)基准+并查集+生成树

引入最近公共祖先LCA

在一棵没有环的树上,除根节点外每个节点都有其父节点和祖先节点,最近公共祖先就是两个节点在这棵树上深度最大的公共祖先节点。寻找两个节点的最近公共节点即根据两个节点的深度分别向树根方向查找,当查找到第一个相同节点时,该节点即为两个节点的最近公共祖先。

排除所有不是桥的边,剩下的即为桥。可以通过判断一条边是否在环上,进行桥的判断。树是边数最小的无环图,并且当向树上添加任意一条顶点都在树上的边,必定会形成环。而桥必定不在环上,一定存在于图的生成树上,所以除了图的生成树上的边,其他的边一定不是桥。基于这一想法,可以先构建生成树,再枚举不在生成树上的所有边,并根据最近公共祖(LCA)排除加入这些边后生成的环所在的边,最后剩下的边即为桥。

 ②生成树:

因为桥边一定会出现在生成树上,所以对于基准法,我们只需要枚举生成树上的边,而不需要枚举所有的边,就能找到答案。生成树优化能够使得枚举边的代价从O(e)变为O(n)。

生成树的构建:使用 DFS 遍历,并在DFS 遍历时根据得到的生成树中边前驱与后继的关系为并查集设置好各个节点的父节点。

环的搜索与桥的标记:引入最近公共祖先来保证向上寻找祖先时每条边只被经过一次。将这些在环中的边标记为非桥。对边的标记可以通过对点数组的操作来实现节省空间。

路径压缩:对于层数较深的节点,需要多次递归才能找到最近公共祖先(LCA),并且,在递归过程中一直沿着完全一样的递归路径进行递归,造成了很多无用的向上递归。运用并查集对路径进行压缩,可以降低层数较深节点的最近公共祖先(LCA)递归时间。

③时间复杂度分析

(顶点个数为n,边个数为)

DFS构建生成树时间复杂度为O(n+e);为并查集设置父节点时间复杂度为O(n);一次查找最近公共祖先最差情况下要查找n次,时间复杂度O(n) ;一次路径压缩最差情况时间复杂度也为O(n);总共需要执行e-m次查找(m为生成树边数),因此算法的总时间复杂为:

 T=O(n+e)+O(n)+(e−m)×O(n)=O(en)

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

即对大数据下算法的时间效率得到了极大提升。

伪代码

LCA(u, v)

if (father[u] == v || father[v] == u)

return

u1 = u, v1 = v;

while(true)

if depth[u]>depth[v]

tag[v]=0,v=father[v]

else

if u!=v

tag[u]=0,tag[v]=0

    u=father[u]

v=father[v]

else break

unzip(u1,u),unzip(v1,v)

Unzip(x, v)

if father[x] == v

return

else

tempx = x

x = father[x];

    father[temp] = v;

Upzip(x, v)

 ⑤时间效率分析

(5)大规模测试

        由于largeG数据集过大,在二维数组过大时使用DFS/BFS递归时可能会出现栈空间不足而无法处理数据从而得到可行解,故先对于IDE进行栈空间的扩充。

        采用优化算法对大规模测试数据进行求解:

 (6)其他优化算法

Tarjan算法是一个基于深度优先搜索(DFS)的图算法,用于寻找一个有向图中的强连通分量。

伪代码:

void Tarjan(G(E,V))

function Tarjan-SCC(v):

  v.index = index

  v.lowlink = index

  index = index + 1

  stack.push(v)

  

  for each edge (v, w) in E:

    if w.index is undefined:

      Tarjan-SCC(w)

      v.lowlink = min(v.lowlink, w.lowlink)

    else if w is in stack:

      v.lowlink = min(v.lowlink, w.index)

  

  if v.lowlink = v.index:

    SCC = []

    repeat

      w = stack.pop()

      SCC.add(w)

    until w = v

    SCCs.add(SCC)

for each vertex v in V:

  if v.index is undefined:

    Tarjan-SCC(v)

算法效率:

基准算法

基准+并查集+生成树

Tarjan

largeG测试运行时间

无解

2.987s

1.692s

通过测试得知,算法效率大大提升。

        除此之外,还可以加入编译优化:由于算法过程中使用了大量的STL容器,因此在编译时应该选择进行O3优化,大致可以将程序的运行时间缩短至原来的一半。

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

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

相关文章

TPCH工具下载及用法

目录 1. 什么是TPCH? 2. 下载TPCH基准工具 3. 编译TPCH基准工具 3.1. 修改头文件 3.2. 修改编译文件 3.3. 执行编译 4. qgen 的用法 4.1. 异常处理 4.2 常见用法 5. dbgen 的用法 5.1. 语法说明 5.2. 常见用法 6. 总结 1. 什么是TPCH? TPC-…

蓝桥杯练习系统(算法训练)ALGO-957 P0703反置数

资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 一个整数的反置数指的是把该整数的每一位数字的顺序颠倒过来所得到的另一个整数。如果一个整数的末尾是以0结尾,那么在它的…

网络基础知识入门

目录 一、局域网与广域网 1、局域网 2、广域网 二、协议 1、概念 2、协议的理解 3、协议的分层 1、分层 2、OSI七层模型 三、网络传输基本流程 1、报头 2、局域网通信原理 3、跨网络传输流程 四、IP地址和MAC地址 1、IP地址 2、MAC地址 3、两者的区别 一、局域…

三种算法实例(二分查找算法、插入排序算法、贪心算法)

当我们听到“算法”这个词时,很自然地会想到数学。然而实际上,许多算法并不涉及复杂数学,而是更多地依赖基本逻辑,这些逻辑在我们的日常生活中处处可见。 在正式探讨算法之前,有一个有趣的事实值得分享:你…

石器时代_单机版_1.0到9.0全部版本集_内附教程

一. 版本介绍图 二. 运行环境 pc单机,可在所有windows系统畅玩,内附安装教程。 三. 源码获取 https://githubs.xyz/y27.html

学浪app中的视频怎么缓存

现在越来越多人在学浪app里面购买课程,有的课程有时间限制,想要下载下来,如果你还不知道下载的方法,可以看看我这篇文章,专门讲解如何缓存学浪app里面的课程 讲技术方法很多人可能听不懂,所以我就将技术融…

【项目实战】【Docker】【Git】【Linux】部署V2rayA项目

今天着手了一个全新领域的项目,从完全没有头绪到成功运行,记录一下具体的部署流程 github项目链接V2rayA 一开始拿到以后完全没有抓手,去阅读了一下他的帮助文档 写着能用docker运行,就去下载了一个Docker配置了一下 拉取代码到…

瑞_23种设计模式_访问者模式

文章目录 1 访问者模式(Visitor Pattern)1.1 介绍1.2 概述1.3 访问者模式的结构1.4 访问者模式的优缺点1.5 访问者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 拓展——双分派4.1 分派4.2 动态分派(多态&am…

4.网络编程-websocket(golang)

目录 什么是websocket golang中使用websocket Server端 Client端 什么是websocket WebSocket是一种在互联网上提供全双工通信的协议,即允许服务器和客户端之间进行双向实时通信的网络技术。它是作为HTML5的一部分标准化的,旨在解决传统HTTP协议在实…

工程中实践的微服务设计模式

大家好,我是 方圆。最近在读《微服务架构设计模式》,开始的时候我非常的好奇,因为在我印象中,设计模式是常说的那23种设计模式,而微服务的设计模式又是什么呢?这个问题也留给大家,在文末我会附上…

opencv图像处理技术(阈值处理与图像平滑)

进行图像处理时,常常需要对图像进行预处理以提取所需的信息或改善图像质量。阈值处理和图像平滑是两种常见的预处理技术。 阈值处理 阈值处理是一种图像分割技术,其基本思想是将图像中的像素值与一个或多个预先设定的阈值进行比较,根据比较…

【人工智能】猫狗识别

猫狗识别 实验背景 数据集介绍 我们使用CIFAR10数据集。CIFAR10数据集包含60,000张32x32的彩色图片,10个类别,每个类包含6,000张。其中50,000张图片作为训练集,10000张作为验证集。这次我们只对其中的猫和狗两类进行预测。 图 1 CIFAR10 数据…

Windows11配置VUE开发环境

目录 一、按照nodejs二、命令安装npm cache clean --forcenpm install -g vue/clinpm install npm -gnpm install webpacknpm install vue-cli -g与npm install -g vue/cli区别npm install -g cnpm --registryhttps://registry.npm.taobao.orgnpm i yarn -g --verbosenpm i -g …

网络与并发编程(一)

并发编程介绍_串行_并行_并发的区别 串行、并行与并发的区别 串行(serial):一个CPU上,按顺序完成多个任务并行(parallelism):指的是任务数小于等于cpu核数,即任务真的是一起执行的并发(concurrency):一个CPU采用时间…

IDEA 导出jar无法执行 错误: 找不到或无法加载主类

1、首先配置正确Project Struct 保证需要引用的jar包库添加到Libraries里,尽管添加到Modules里依然可以测试运行或调试,但导出的jar包会遇到问题。 2、导出jar,方式选择如下 选择”From modules with dependencies" 然后去掉以上“Extr…

手机如何在线制作gif?轻松一键在线操作

现在大家都喜欢使用手机来拍摄记录有趣的事物,但是时间长了手机里的视频越来越多导致手机存储空间不够了,这些视频又不想删除时应该怎么办呢?这个很简单,下面就给大家分享一款不用下载手机就能操作的视频转gif网站-GIF中文网&…

【贪玩巴斯】Mac的M芯片(M1/2...)下载homebrew方法(24年最新且已验证可行)

1. 按照目前广为流传的方法(M1会出现一些问题): 终端输入: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 使用国内镜像下载。 2. 输入后按照要求步骤执行即可&#xff…

k8s单节点部署,容器运行时使用containerd

环境 系统 : entOS Linux release 7.9.2009 (CoreIP:192.168.44.177 硬件要求:控制平面最少需要 2c2g 安装前环境准备 如果是集群部署还需要配置时间同步 关闭防火墙 systemctl disable firewalld关闭selinux setenforce 0sed -i s/SELI…

Vue - 你知道Vue组件中的data为什么是一个函数吗

难度级别:中高级及以上 提问概率:80% 在Vue项目中,App.vue下的每个子组件都会生成一个单独的Vue实例对象,但这些子对象都是通过通过vue.extend方法创建而来的,也就是说我们平时在项目中所定义的Vue组件,都有一个相同的父类对象。这样也就…

JS-PC/MOBILE技巧--PC

一、PC 端网页特效 1、元素偏移量 offset 系列 offset 概述 offset 就是偏移量,使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。 获取元素距离带有定位父元素的位置获取元素自身的大小(宽度高度&#xff0…