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

news2024/12/23 7:12:23

目录

一、实验目的

二、问题描述

三、实验要求

四、算法思想

1.  基准算法

1.1 算法思想

1.2 代码

1.3 时间复杂度

2. 使用并查集的高效算法

2.1 算法思想

2.2 代码:

2.3 时间复杂度:

五、实验结果


实验目的

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.  基准算法

1.1 算法思想

        1)先dfs遍历图,得到连通分量个数N;

        2)遍历边集,对于每条边ei,先删除ei;

        3)再次dfs得到此时的连通分量个数num,

        4)如果num!=N,则ei为桥;否则不为桥。

        5)恢复ei,取下一条边ei+1,回到2)继续,直到遍历完全部边。

1.2 代码
int **k,*v,n,m,**br,brnum,N;
//邻接矩阵、访问位、点数、边数、是否为桥、桥数、
int *p,*d,*qiao;//并查集、点的深度、标记dfs2后是否为桥
string filename;//文件名

void read1(){//初始化
	int i,a,b;
	ifstream file(filename.c_str());
	file>>n>>m;
	k=new int*[n];
	v=new int[n];
	br=new int*[n];
	brnum=0;
	for(i=0;i<n;i++){
		k[i]=new int[n];
		br[i]=new int[2];
		v[i]=0;
		br[i][0]=br[i][1]=0;
		for(int j=0;j<n;j++)
			k[i][j]=0;
	}
	for(i=0;i<m;i++){
		file>>a>>b;
		k[a][b]=1;
		k[b][a]=1;
	}
	file.close();
}

void dfs1(int a){//dfs深度遍历
	int i;
	v[a]=1;
	for(i=0;i<n;i++)
		if(k[a][i]&&!v[i])
			dfs1(i);
}

int count1(){//计算连通分量个数
	int i,num=0;
	for(i=0;i<n;i++)
		if(!v[i]){
			dfs1(i);
			num++;
		}
 	return num;
}

void base(){//基准法
	int i,j,a,b,num;
	ifstream file(filename.c_str());
	file>>n>>m;
	N=count1();
	for(i=0;i<m;i++){
		file>>a>>b;
		k[a][b]=0;//先删除该边
		k[b][a]=0;
		for(j=0;j<n;j++)
			v[j]=0;
		num=count1();
		if(num!=N){//判断是否为桥
			br[brnum][0]=a;//记录桥
			br[brnum][1]=b;
			brnum++;//桥个数增加
		}
		k[a][b]=1;//恢复该边
		k[b][a]=1;
	}
	file.close();
}

void print1(){//因为实验对输出格式有要求
	int i,j,a,b;
	cout<<brnum<<endl;
	for(i=0;i<brnum-1;i++){//选择排序
		a=i;
		for(j=i+1;j<brnum;j++){
			if(br[j][0]<br[a][0]||
				(br[j][0]==br[a][0]&&br[j][1]<br[a][1]))
				//父端较小 或 父端相同、子端较小
			a=j;
		}
		b=br[a][0];
		br[a][0]=br[i][0];
		br[i][0]=b;
		b=br[a][1];
		br[a][1]=br[i][1];
		br[i][1]=b;
	}
	for(int i=0;i<brnum;i++){
		cout<<br[i][0]<<" "<<br[i][1]<<endl;
	}
}
1.3 时间复杂度

        n个点,m条边。需要遍历m条边,O(m),每次都需要count1一次,而count1由于是使用邻接矩阵储存边关系,最坏情况为O(n^2)。所以总的时间复杂度为O(m*n^2)。

        如果用邻接表,则count1的时间复杂度为O(n+m),总时间复杂度变为O(m(n+m))。

2. 使用并查集的高效算法

2.1 算法思想

        桥的等价意义:不在环上的边

        树是边数最小的无环图,当向树上添加任意一条顶点都在树上的边时,会形成环。桥不在环上,所以桥只能在图的生成树上。

        所以还是先构建生成树,然后不断dfs,不过dfs过程中顺便记录每个点的深度d[i]。

        再遍历所有边,每次遍历时:

                如果为生成树上的边则直接return。

                否则:求当前边两端点的最近公共祖先(LCA),过程中将路过的边(在环上)置为非桥边(q[i]==0);并根据LCA进行路径压缩compress(将环上除了LCA本身的点的父节点均设置为LCA)。

        这里以点带边,即q[i]==1表示以第i个点为尾的生成树上的边为桥,该边用(p[i],i)表示(p[i]为i在生成树上的父节点)。

2.2 代码:
void read2(){//读入文件信息并初始化
	…………
}

void dfs2(int a,int b,int depth){//b is the ancestor of a
	…………
}

void count2(){//生成生成树、并查集、深度集合等
	…………
}

void compress(int x,int a){//路径压缩
	if(p[x]==a)//等于最近公共祖先
		return;
 	else{
 		int t=x;
 		x=p[x];
 		p[t]=a;
 		compress(x,a);
	}
}

void LCA(int a,int b){//对每条边的两点找最近公共祖先
	if(p[a]==b||p[b]==a)//在生成树上的边,直接返回
		return;
	else{
		int u=a,v=b,//保留原边的两端
			x=0,y=0;//判断a、b是否在while中执行了a=p[a]、b=p[b]操作,有执行才压缩,
					//避免其中一点是最近公共祖先时压缩导致该点父节点被自己覆盖
		while(1){
			if(d[a]>d[b]){//深度大的向上遍历
				qiao[a]=0;
				a=p[a];
				x=1;
			}
			else if(d[a]<d[b]){//深度大的向上遍历
				qiao[b]=0;
				b=p[b];
				y=1;
			}
			else if(a!=b){//深度相同但不同点,一起向上遍历
				qiao[a]=0;
				qiao[b]=0;
				a=p[a];
				b=p[b];
				x=y=1;
			}
			else break;//同个点,a=b=最近公共祖先
		}
        //此时a==b
		if(x)//路径压缩
		compress(u,a);
		if(y)
		compress(v,b);
	}
}
void better(){//并查集的高效算法
	int i,a,b;
	ifstream file(filename.c_str());//读入文件
	count2();//生成生成树、并查集、深度集合等
	file>>n>>m;
	for(i=0;i<m;i++){//遍历每一条边
		file>>a>>b;
		LCA(a,b);//求最近公共祖先
	}
	file.close();
}
void print2(){//打印输出
	……
}

     虽然压缩后不能再直接使用”if(p[a]==b||p[b]==a)“判断边(a,b)是否为原生成树上的边,但不影响结果。因为压缩的边都是非桥边,只不过会执行下面的while。但总体上压缩后效率还是提高了的。

2.3 时间复杂度:

        n个点,m条边。dfs构建生成树最坏情况下时间复杂度为O(n^2)。遍历m条边,每次查找最近公共祖先最差情况下要查找n次,时间复杂度为O(n),一次路径压缩最差情况时间复杂度也为O(n)。所以总时间复杂度为O(n*(m+n))。

实验结果

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

                                                  

        验证正确。

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

        mediumG.txt:

       对于largeG.txt文件,由于使用的是邻接矩阵,n太大,运行时内存不够分配,运行中断。

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

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

相关文章

《代码大模型安全风险防范能力要求及评估方法》正式发布

​代码大模型在代码生成、代码翻译、代码补全、错误定位与修复、自动化测试等方面为研发人员带来了极大便利的同时&#xff0c;也带来了对安全风险防范能力的挑战。基于此&#xff0c;中国信通院依托中国人工智能产业发展联盟&#xff08;AIIA&#xff09;&#xff0c;联合开源…

干货分享 | TSMaster 的 CAN UDS 诊断操作指南(下)

上期&#xff0c;我们主要介绍了 UDS 诊断模块的创建以及TSMaster 基础诊断配置。很多客户表示意犹未尽。因此我们将继续带来《TSMaster 的 CAN UDS 诊断操作指南&#xff08;下&#xff09;》的精彩内容&#xff0c;为您带来UDS on CAN/CAN FD 的功能以及详细的使用操作。 本文…

深入浅出:npm常用命令详解与实践

简介 在现代的软件开发中&#xff0c;特别是在 JavaScript 生态系统中&#xff0c;npm&#xff08;Node Package Manager&#xff09;是一个核心工具。它不仅仅是 Node.js 的包管理器&#xff0c;还扮演着项目依赖管理、脚本执行、项目发布等多重角色。理解 npm 的常用命令不仅…

【数据结构】线性表之《队列》超详细实现

队列 一.队列的概念及结构二.顺序队列与链队列1.顺序队列2.链队列 三.链队列的实现1.创建队列2.初始化队列3.入队4.出队5.获取队头元素6.获取队尾元素7.队列的大小8.队列的判空9.清空队列10.销毁队列 四.队列的盲区五.模块化源代码1.Queue.h2.Queue.c3.test.c 六.栈和队列必做O…

小白上手AIGC-基于FC部署stable-diffusion

AIGC AIGC&#xff08;人工智能创造内容&#xff09;作为一种基于人工智能技术生成内容的新型创作模式。打破了过去大家对于AI的理解都是说只能涉足部分领域而无法涉足艺术或者是其他的创作领域的定律&#xff0c;现在的AIGC也能够创作内容了&#xff0c;而不再只是单纯的返回…

MAC Address

文章目录 1. 前言2. MAC Address2.1 MAC 地址格式2.2 Locally Administered MAC Address2.3 MAC 单播 和 多播 3. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. MAC Address 2.1 MA…

Java集合框架深度解析:Hashtable、HashMap与TreeMap的较量,哪个更适合你的项目?

引言 在Java编程世界中&#xff0c;集合是编程语言的重要组成部分。它们负责存储、组织和操作数据集合&#xff0c;是开发过程中不可或缺的工具。Java集合框架提供了丰富且功能强大的数据结构&#xff0c;而其中的Hashtable、HashMap和TreeMap是使用频率极高的三种实现。 本篇文…

2024年希望杯数学竞赛各年级100道练习题及答案

链接里面有无答案版本链接&#xff1a;https://pan.baidu.com/s/1nTIVJrTEWUzb0LJNo4mI_Q 提取码&#xff1a;0548 –来自百度网盘超级会员V7的分享 一年级 二年级 三年级 四年级 五年级 六年级 七年级 八年级

如何使得Macos的剪切板感知fileURL并当fileURL被执行paste 动作时 回调到某个监听的函数 从而来填充file content

问题及尝试&#xff1a; 我在做一个跨平台文件拷贝的功能&#xff0c;文件可能是从其他操作系统比如Linux 或者Windows 拷贝到Macos上&#xff0c; 但是我试过所有可以hook NSPasteboard的方法&#xff0c;确实没有找到可以监听macos 剪切板的方法&#xff0c;因为fileURL 确实…

网络设备框架

文章目录 前言一、主要流程二、Linux网络设备驱动架构1.概述2.读入数据 总结 前言 Linux中的Ethernet驱动框架涉及到网络设备驱动程序的多个方面&#xff0c;包括初始化、注册、数据传输以及与物理层&#xff08;PHY&#xff09;的交互。以下是网络设备驱动架构的概述&#xf…

Centos7虚拟机

Centos 7 安装 1 镜像下载1.1 官网下载1.2 阿里云镜像下载 2 环境的安装2.1 打开我们的虚拟机&#xff0c;点击文件进行新建2.2 选择典型之后&#xff0c;下一步2.3 选择稍会安装操作系统2.4 勾选Linux&#xff0c;并且选择CentOS 7的版本2.5 设定我们虚拟机的名称和安装位置2.…

VOSviewer分析知网文献

VOSviewer简介 VOSviewer 是一款用于构建和可视化科学文献计量网络的软件工具。它能够帮助用户分析和可视化期刊、研究人员或单个出版物之间的关系&#xff0c;这些关系可以基于引用、共引、共著或术语共现关系来构建。VOSviewer 还提供了文本挖掘功能&#xff0c;可以用来构建…

Python 爬虫从入门到入狱之路一

实际上爬虫一共就四个主要步骤&#xff1a; 明确目标 (要知道你准备在哪个范围或者网站去搜索)爬 (将所有的网站的内容全部爬下来)取 (去掉对我们没用处的数据)处理数据&#xff08;按照我们想要的方式存储和使用&#xff09; 我们在之前写的爬虫程序中&#xff0c;都只是获取…

fidder自动测试cookie脚本

前言 工作在使用fidder抓包时&#xff0c;经常需要找到一个请求携带的cookie中&#xff0c;真正校验了那些cookie&#xff0c;从而在代码中实现写入这些cookie的请求。这个过程除了根据经验快速过滤&#xff0c;就只能一个一个删除测试了。 所以我写了这个脚本&#xff0c;自动…

阿里云云服务器、ACR镜像服务、容器化实战:搭建企业应用

一、容器化基础知识 华为云免费试用服务器&#xff1a;https://activity.huaweicloud.com/free_test/index.html 阿里云docker容器教程&#xff1a;https://edu.aliyun.com/course/3111900/lesson/341807097 查询ip地址&#xff1a;www.ip138.com 二、容器化搭建企业应用实战 2…

如何选择和优化谷歌外贸关键词?

长尾关键词是关键&#xff0c;长尾关键词是指由三个或更多词组成的更具体、更详细的搜索词组。与单个关键词相比&#xff0c;长尾关键词虽然搜索量较低&#xff0c;但往往能带来更高的转化率&#xff0c;因为它们更能精准地反映用户的搜索意图和需求 使用长尾关键词有几个优势…

海南云亿商务咨询有限公司抖音带货怎么样?

在数字化浪潮席卷全球的今天&#xff0c;电商行业正迎来前所未有的发展机遇。特别是短视频平台如抖音的崛起&#xff0c;更是为电商行业注入了新的活力。海南云亿商务咨询有限公司&#xff0c;作为抖音电商服务的佼佼者&#xff0c;凭借其专业的团队和卓越的服务&#xff0c;助…

北邮《计算机网络》蒋老师思考题及答案-传输层

蒋yj老师yyds&#xff01; 答案自制&#xff0c;仅供参考&#xff0c;欢迎质疑讨论 问题一览 传输层思考题P2P和E2E的区别使用socket的c/s模式通信&#xff0c;流控如何反映到编程模型三次握手解决什么问题举一个两次握手失败的例子为什么链路层是两次握手而非三次&#xff1f;…

HTML(24)——过渡

过渡 作用&#xff1a;可以为一个元素在不同的状态之间切换的时候添加过渡效果 属性名&#xff1a;transition(复合属性) 属性值&#xff1a;过渡的属性 花费时间(s) 提示&#xff1a; 过渡的属性可以是具体的CSS属性也可以为all&#xff08;两个状态属性值不同的所有属性…

证件照制作工具有哪些?分享当下热门的证件照制作工具

无论是考证、出国旅游还是应聘&#xff0c;一张符合标准的证件照成了必备之物。 如果手头的证件照尺寸不符合要求&#xff0c;不必惊慌&#xff0c;现在有多种证件照制作软件可以帮助你迅速解决问题。 今天&#xff0c;本文就为大家分享几个证件照制作教程&#xff0c;让你的…