最小生成树Kruskal、Prim算法C++

什么是最小生成树

连通图:

在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1和顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。

生成树:

一个连通图的最小连通子图称作为图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。

最小生成树:

最小生活树是生成树的一个特殊情况,它的边权之和最小。其特点如下:

  1. 只能使用图中权值最小的边来构造最小生成树
  2. 只能使用恰好n-1条边来连接图中的n个顶点
  3. 选用的n-1条边不能构成回路(构成回路会导致有顶点为连通或权值过大)

最小生成树的实际应用

例如城市道路铺设中,如果我们直接使用连通结构,这样两点间的交通必然是最便捷的。可是修路的成本的巨大的,但是又要连通所有城市,这便会想到使用最小生成树。

考虑到经济发达城市人口多、车辆多,我们还需要为其多修建一些道路,则实际中的道路修建与最小生成树的结果不同,但是最小生成树在这些实际场景发挥了很大作用。

Kruskal算法

任给一个有n个顶点的连通网络N = {V,E}。

首先构造一个由这n个顶点组成、不含任何边的图G={V,null},其中每个顶点自成一个连通分量,其次不断从E中取出权值最小的一条边(若有多条任取其中之一),若该边的两个顶点来自不同的连通分量,则将此边加入到G中。如此重复,知道所有顶点在同一个连通分量上为止。

算法思路

核心:每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树。

将权值小的边放入到优先级队列中。依次类推

接下来有一个问题,因为生成树不能构成回路,所以在添加边的时候要处理成环问题。

这个问题的解决就要使用并查集;如果该边已经出现过了,则不选择该边,如果该边不在集合中,则可以选择该边

以下是选边的全部过程:

代码实现

首先我们要创建一个边的数据结构Edge,用于将边存放到优先级队列中。

struct Edge
{size_t _srci;size_t _dsti;W _w;Edge(size_t srci, size_t dsti, W w):_srci(srci), _dsti(dsti), _w(w){}//还要提供一个比较函数,因为优先级队列中使用到了greater,greater会调用>函数bool operator > (const Edge& ed) const{return _w > ed._w;}
};

实现思路:

1. 将所有的边统计到优先级队列中,(注意临界矩阵只遍历一半)

2. 从优先级队列中选出n-1条边,n为顶点数量

3. 依次取出队列中的元素,判断该元素是否出现过(使用并查集)

4. 如果没有出现过,则添加该边到最小生成树中,并将该边添加到并查集中,再统计权值。

5. 当队列元素取空时,最小生成树中边的数量小于n-1,则表示该图没有连通,则没有最小生成树,直接返回权值的默认值即可。

6. 如果size等于n-1,则表示最小生成树创建成功,返回权值总和即可

//最小生成树
//如果有最小生成树,则返回该树的权值,如果没有最小生成树,则返回默认值
W Kruskal(Self& minTree)
{//初始化minTreesize_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix = vector<vector<W>>(n, vector<W>(n, MAX_W));//1. 将边统计起来,使用优先级队列或排序的方式都可以   priority_queue<Edge, vector<Edge>, greater<Edge>> minque;for (size_t i = 0; i < n; i++){//矩阵中走一半即可  要不然会重复入队列for (size_t j = 0; j < i; j++){if (_matrix[i][j] != MAX_W){minque.push(Edge(i, j, _matrix[i][j]));}}}//2.选出n-1条边size_t size = 0;UnionFindSet ufs(n);   //n个顶点W total_W{};            //总的权值while (!minque.empty() && size < n){Edge min = minque.top();minque.pop();//3.选出一条边之后看该边在不在当前集合,//在就不选择该边,不在就选择该边,并标记if (!ufs.IsInset(min._srci, min._dsti)){cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] <<":" << _matrix[min._srci][min._dsti] << endl;minTree._AddEdge(min._srci, min._dsti, min._w);ufs.Union(min._srci, min._dsti);size++;total_W += min._w;}}//如果该图不是连通图,则没有最小生成树if (size < n - 1){return W();}return total_W;
}

接下来是一些要注意的点:

1. 要将最小生成树进行初始化,否则添加边时会出现越界问题。

2. 最小生成树其实就是该图的子图,typedef Graph<V, W, MAX_W, Direction> Self; 

3. 注意将顶点放入到并查集中,防止边的重复。

测试用例

void TestGraphMinTree()
{const char* str = "abcdefghi";matrix::Graph<char, int> g(str, strlen(str));g.AddEdge('a', 'b', 4);g.AddEdge('a', 'h', 8);//g.AddEdge('a', 'h', 9);g.AddEdge('b', 'c', 8);g.AddEdge('b', 'h', 11);g.AddEdge('c', 'i', 2);g.AddEdge('c', 'f', 4);g.AddEdge('c', 'd', 7);g.AddEdge('d', 'f', 14);g.AddEdge('d', 'e', 9);g.AddEdge('e', 'f', 10);g.AddEdge('f', 'g', 2);g.AddEdge('g', 'h', 1);g.AddEdge('g', 'i', 6);g.AddEdge('h', 'i', 7);matrix::Graph<char, int> kminTree;cout << "Kruskal:" << g.Kruskal(kminTree) << endl;kminTree.Print();
}

Prim算法

算法思路

Prim算法所具有的一个性质是集合A中的边总是构成一棵树。这棵树从一个任意的根节点r开始,一直扩大到图中的所有顶点为止。在每一步连接集合A和A之外的顶点的所有边中,选择一条权值最小的边加入到A中。当算法结束时,A中的边形成一棵最小生成树。

因为添加边只会在当前集合中没有的顶点中进行,所以Prim算法天然避免环的生成,不需要使用并查集来避免环的产生。

代码实现

实现思路:

1. 使用两个数组表示X、Y集合,用于表示当前顶点是否被访问。

2. 从X集合中的顶点选出所有的边,可以使用优先级队列来存放边。

3. 依次从优先级队列中选出权值最小的边

4. 判断该边是否成环---即dest顶点必须在Y集合中

5. 在Y集合中则将该边添加到最小生成树中,然后进行顶点的标记

6. 再将dest顶点连通的边再放入队列中,同时也要判断dest连接的顶点归属Y集合。

7. 如果选出n-1条边,返回生成树的总权值,否则返回默认值


W  Prim(Self& minTree, const W& src)   //src表示从哪个起点开始
{size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();//初始化minTreeminTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix = vector<vector<W>>(n, vector<W>(n, MAX_W));//两个数组表示X、Y集合,用于表示当前顶点是否被访问vector<bool> X(n, false);vector<bool> Y(n, true);//初始化X,Y集合X[srci] = true;Y[srci] = false;//从X-Y集合连接中的边选出权值最小的边priority_queue<Edge, vector<Edge>, greater<Edge>> minque;//先把srci连接的边添加到队列中for (size_t i = 0; i < n; i++){if (_matrix[srci][i] != MAX_W){minque.push(Edge(srci, i, _matrix[srci][i]));}}size_t size = 0;W total_W = W();while (!minque.empty()){Edge min = minque.top();minque.pop();if (Y[min._dsti])  //该顶点必须还在Y集合中   注意!!{cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] <<":" << _matrix[min._srci][min._dsti] << endl;minTree._AddEdge(min._srci, min._dsti, min._w);total_W += min._w;size++;if (size == n - 1)break;X[min._dsti] = true;Y[min._dsti] = false;//将dsti的边进行遍历for (size_t i = 0; i < n; i++){if (_matrix[min._dsti][i] != MAX_W && Y[i])    //有连通,并且是Y集合中的顶点{minque.push(Edge(min._dsti, i, _matrix[min._dsti][i]));}}}}//如果该图不是连通图,则没有最小生成树if (size < n - 1){return W();}return total_W;
}

测试用例

void TestGraphMinTree()
{const char str[] = "abcdefghi";matrix::Graph<char, int> g(str, strlen(str));g.AddEdge('a', 'b', 4);g.AddEdge('a', 'h', 8);g.AddEdge('b', 'c', 8);g.AddEdge('b', 'h', 11);g.AddEdge('c', 'i', 2);g.AddEdge('c', 'f', 4);g.AddEdge('c', 'd', 7);g.AddEdge('d', 'f', 14);g.AddEdge('d', 'e', 9);g.AddEdge('e', 'f', 10);g.AddEdge('f', 'g', 2);g.AddEdge('g', 'h', 1);g.AddEdge('g', 'i', 6);g.AddEdge('h', 'i', 7);/*matrix::Graph<char, int> kminTree;cout << "Kruskal:" << g.Kruskal(kminTree) << endl;kminTree.Print();*/cout << "prim算法的实现" << endl;matrix::Graph<char, int> pminTree;cout << "Prim:" << g.Prim(pminTree, 'a') << endl;pminTree.Print();
}

最后细心的你可以会发现,两种算法的结果权值都是37,但是其生成的最小生成树是不同的,这就好比一个正三角形,三个顶点所在的位置,无论连接哪条线,其都是最小生成树,所以不唯一。

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

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

相关文章

ARM编程模型-寄存器组

Cortex A系列ARM处理器共有40个32位寄存器,其中33个为通用寄存器,7个为状态寄存器。usr模式和sys模式共用同一组寄存器。 通用寄存器包括R0~R15,可以分为3类: 未分组寄存器R0~R7分组寄存器R8~R14、R13(SP) 、R14(LR)程序计数器PC(R15)、R8_fiq-R12_fir为快中断独有 在不同模…

centos中得一些命令 记录

redis命令 链接redis数据库的命令 redis-cli如果 Redis 服务器在不同的主机或端口上运行&#xff0c;你需要提供相应的主机和端口信息。例如&#xff1a; redis-cli -h <hostname> -p <port>连接成功后&#xff0c;你将看到一个类似于以下的提示符&#xff0c;表…

手写Mybatis:第12章-完善ORM框架,增删改查操作

文章目录 一、目标&#xff1a;完善增删改查二、设计&#xff1a;完善增删改查三、实现&#xff1a;完善增删改查3.1 工程结构3.2 完善增删改查类图3.3 扩展解析元素3.4 新增执行方法3.4.1 执行器接口添加update3.4.2 执行器抽象基类3.4.3 简单执行器 3.5 语句处理器实现3.5.1 …

【Eclipse】Project interpreter not specified 新建项目时,错误提示,已解决

目录 0.环境 1&#xff09;问题截图&#xff1a; 2&#xff09;错误发生原因&#xff1a; 1.解决思路 2.具体步骤 0.环境 windows 11 64位&#xff0c;Eclipse 2021-06 1&#xff09;问题截图&#xff1a; 2&#xff09;错误发生原因&#xff1a; 由于我手欠&#xff0c;将…

YOLOV8实例分割——详细记录环境配置、自定义数据处理到模型训练与部署

前言 Ultralytics YOLOv8是一种前沿的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它在前代YOLO版本的成功基础上进行了进一步的创新&#xff0c;引入了全新的特性和改进&#xff0c;以进一步提升性能和灵活性。作为一个高速、精准且易于操作的设计&#xff0c;YO…

搭建个人hMailServer 邮件服务实现远程发送邮件

文章目录 1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpolar内网映射工…

拓扑排序算法 -- dfs、bfs

210. 课程表 II 该题用到「拓扑排序」的算法思想&#xff0c;关于拓扑排序&#xff0c;直观地说就是&#xff0c;让你把⼀幅图「拉平」&#xff0c;⽽且这个「拉平」的图⾥⾯&#xff0c;所有箭头⽅向都是⼀致的&#xff0c;⽐如上图所有箭头都是朝右的。 很显然&#xff0c;如…

视频汇聚/视频云存储/视频监控管理平台EasyCVR部署后无法正常启用是什么问题?该如何解决?

安防监控/视频监控/视频汇聚平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;视频云存储/安防监控汇聚平台EasyCVR支持多种播放协议&#xff0c;包括&#xff1a;HLS、HTTP-FLV、WebSoc…

物联网应用中蓝牙模块怎么选?_蓝牙模块厂家

在蓝牙模块选型前期&#xff0c;一定要了解应用场景以及需要实现的功能&#xff08;应用框图&#xff09;&#xff0c;以及功能实现过程中所能提供调用的接口&#xff08;主从设备&#xff0c;功能&#xff09;&#xff0c;考虑模块供电&#xff0c;尺寸&#xff0c;接收灵敏度…

“深入探究SpringMVC的工作原理与入门实践“

目录 引言1. 什么是SpringMVC?1.1. 模型1.2. 视图1.3. 控制器 2. SpringMVC的工作流程2.1. 客户端发送请求2.2. DispatcherServlet的处理2.3. 处理器映射器的使用2.4. 处理器的执行2.5. 视图解析器的使用2.6. 视图的渲染 3. SpringMVC的核心组件4. 弹簧MVC总结 引言 SpringMV…

GitHub打不开解决方法——授人以渔

打不开GitHub的原因之一&#xff0c;DNS地址解析到了无法访问的ip。&#xff08;为什么无法访问&#xff1f;&#xff09; 1、打开GitHub看是哪个域名无法访问&#xff0c;F12一下 2、DNS解析看对应的域名目前哪个IP可以访问 DNS解析的网址&#xff1a; &#xff08;1&#x…

3D开发工具HOOPS Publish如何快速创建交互式3D PDF文档?

HOOPS Publish是一款功能强大的SDK&#xff0c;可以创作丰富的工程数据并将模型文件导出为各种行业标准格式&#xff0c;包括PDF、STEP、JT和3MF。HOOPS Publish核心的3D数据模型是经过ISO认证的PRC格式(ISO 14739-1:2014)&#xff0c;它为装配树、拓扑和几何、产品制造信息和视…

将序数与比特币智能合约集成:第 1 部分

将序数与比特币智能合约集成&#xff1a;第 1 部分 最近&#xff0c;比特币序数在区块链领域引起了广泛关注。 据称&#xff0c;与以太坊 ERC-721 等其他代币标准相比&#xff0c;Ordinals 的一个主要缺点是缺乏对智能合约的支持。 我们展示了如何向 Ordinals 添加智能合约功…

分布式 - 服务器Nginx:基础系列之Nginx静态资源优化配置指令sendfile | tcp_nopush | tcp_nodelay

文章目录 1. sendfile 指令2. tcp_nopush 指令3. tcp_nodelay 指令 1. sendfile 指令 请求静态资源的过程&#xff1a;客户端通过网络接口向服务端发送请求&#xff0c;操作系统将这些客户端的请求传递给服务器端应用程序&#xff0c;服务器端应用程序会处理这些请求&#xff…

【综述+3D】基于NeRF的三维视觉2023年度进展报告(截止2023.06.10)

论文&#xff1a;2003.Representing Scenes as Neural Radiance Fields for View Synthesis 官方网站&#xff1a;https://www.matthewtancik.com/nerf 突破性后续改进&#xff1a; Instant Neural Graphics Primitives with a Multiresolution Hash Encoding | 展示官网&#…

故障分析 | OceanBase 频繁更新数据后读性能下降的排查

以下文章来源于爱可生开源社区 &#xff0c;作者张乾 爱可生开源社区. 爱可生开源社区&#xff0c;提供稳定的MySQL企业级开源工具及服务&#xff0c;每年1024开源一款优良组件&#xff0c;并持续运营维护。 测试在做 OceanBase 纯读性能压测的时候&#xff0c;发现对数据做过…

MySQL表的内连和外连

文章目录 MySQL表的内连和外连1. 内连接(1) 显示SMITH的名字和部门名称 2. 外连接2.1 左外连接(1) 查询所有学生的成绩&#xff0c;如果这个学生没有成绩&#xff0c;也要将学生的个人信息显示出来 2.2 右外连接(1) 对stu表和exam表联合查询&#xff0c;把所有的成绩都显示出来…

如何使用蚂蚁集团自动化混沌工程 ChaosMeta 做 OceanBase 攻防演练?

当前&#xff0c;业界主流的混沌工程项目基本只关注如何制造故障的问题&#xff0c;而经常做演练相关工作的工程师应该明白&#xff0c;每次演练时还会遇到以下痛点&#xff1a; 检测当前环境是否符合演练预设条件&#xff08;演练准入&#xff09;&#xff1b; 业务流量是否满…

Apache Linkis 与 OceanBase 集成:实现数据分析速度提升

导语&#xff1a;恭喜 OceanBase 生态全景图中又添一员&#xff0c;Apache Linkis 构建了一个计算中间件层&#xff0c;以促进上层应用程序和底层数据引擎之间的连接、治理和编排。 近日&#xff0c;计算中间件 Apache Linkis 在其新版本中通过数据源功能&#xff0c;支持用户通…

vue仿企微文档给页面加水印(水印内容可自定义,超简单)

1.在src下得到utils里新建一个文件watermark.js /** 水印添加方法 */let setWatermark (str1, str2) > {let id 1.23452384164.123412415if (document.getElementById(id) ! null) {document.body.removeChild(document.getElementById(id))}let can document.createE…