图论中的最小生成树:Kruskal与Prim算法深入解析

 

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:アンビバレント—Uru

                                                                0:24━━━━━━️💟──────── 4:02
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


 

目录

最小生成树

Kruskal算法

如何理解Kruskal算法?

Kruskal算法的实现

Prim算法

如何理解Prim算法?

Prim算法的实现


最小生成树

        最小生成树(Minimum Spanning Tree,简称 MST)是一个无向连通图中包含所有顶点的边的权值之和最小的子图。它有以下特点:

  1. 包含原图所有顶点:最小生成树是一个生成树,因此它必须包含图中的所有顶点。
  2. 边的权值之和最小:在所有的生成树中,最小生成树是边的权值总和最小的那一个。
  3. 是无环的:作为一棵树,最小生成树中不存在环,即任意两个顶点之间有且仅有一条路径相连。

        求解最小生成树的常用算法有两种:

  • Kruskal算法:该算法的基本思想是按照边的权值从小到大的顺序选择边,如果这条边连接的两个顶点不在同一个连通分量中,则加入这条边,直到所有顶点都被包含在内。这个过程中,使用并查集数据结构来判断两个顶点是否属于同一个连通分量是非常有效的。
  • Prim算法:与Kruskal算法不同,Prim算法是一种贪心算法,它从任意一个顶点开始,每一步都选择连接当前已选取顶点集合与未选取顶点集合之间权值最小的边,并将其加入到最小生成树中,直到所有顶点都被包含。

        总的来说,最小生成树的概念在许多领域都有应用,如网络设计、交通规划等,它可以帮助找到成本最低的连接方案。

Kruskal算法

如何理解Kruskal算法?

        Kruskal算法是一种用来寻找最小生成树的贪心算法,它的核心思想是选择边权值之和最小的同时不会形成环路的边来构建最小生成树。(使用的就是全局的贪心策略)具体步骤如下:

  1. 边的排序:将图中的所有边按照权值从小到大进行排序。(这一步我们可以使用到优先级队列)
  2. 初始化并查集:使用并查集数据结构来记录各个顶点的连通性,初始时每个顶点自成一个集合。
  3. 遍历边并检查环路:按权值从小到大的顺序遍历每条边,利用并查集判断这条边连接的两个顶点是否属于不同的集合。如果是,则加入这条边并不会形成环路,因此将其加入到最小生成树中,并将这两个顶点所在的集合合并。
  4. 重复直至完成:重复上述步骤,直到添加了V-1条边(V为图中顶点的数量),此时便形成了包含所有顶点的最小生成树。

        需要注意的是,在应用Kruskal算法时,需要确保图是连通的,因为算法只能处理连通图的最小生成树问题。如果图不是连通的,那么需要先对图进行连通分量的分析,然后对每个连通分量分别求解最小生成树。大致图解如下:

Kruskal算法的实现

        本文的Kruskal算法是在邻接矩阵的基础上进行实现的,邻接矩阵的实现参考上一篇文章,这里先实现边的结构体,给定两个size_t类型表示源地址_srcI以及目标地址_dstI(实际上就是对应顶点的下标),根据模版类型W来存储边的权值_w,并且重载了一个>符,用于后续优先级队列中对于伪函数的使用,实现如下:

		struct Edge{size_t _srci;size_t _dsti;W _w;Edge(size_t srci, size_t dsti, const W& w):_srci(srci), _dsti(dsti), _w(w){}bool operator>(const Edge& e) const{return _w > e._w;}};

        需要注意:前面我们将整个邻接矩阵都已经重新命名了,这样做是为了更好的定义最小生成树,毕竟他本质上也是一个图,并且也是在原来的基础上定义的,我们当然可以使用原来的数据结构:

typedef Graph<V, W, MAX_W, Direction> Self;

        接下来,按照上述的实现步骤一步一步实现:(1)初始化,由于最小生成树对于原图可能只是变小于等于原图,其他实际上都是一样的。因此,我们可以根据原图进行初始化,顶点的存储、顶点同下标的映射都是一样的,只是边需要重新构建!其中MAX_W是作为初始化边的值,默认为INT_MAX。(2)使用优先级队列,以升序的方式对上面我们构建的边结构体进行排序,将原图的所有边都放入优先级队列!(3)选出n-1条边,首先定义一个变量size用于储存有多少条边被选,定义一个totalW用于储存权值,定义一个并查集ufs用于查环。每次遍历都会将优先级队列的顶部值取出,上一篇文章中提到过InSet是用于判断是否为同一根节点(判环)的操作,若不为环则按照邻接矩阵的方法构造边,并且也在并查集中合并在一起。(4)这个循环中不论如何都会将优先级队列中的值全部释放,然后退出循环,最后根据size来判断是否可以形成最小生成树并且返回权值。如下为具体的实现:

		W Kruskal(Self& minTree){size_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n);for (size_t i = 0; i < n; ++i){minTree._matrix[i].resize(n, MAX_W);}priority_queue<Edge, vector<Edge>, greater<Edge>> minque;for (size_t i = 0; i < n; ++i){for (size_t j = 0; j < n; ++j){if (i < j && _matrix[i][j] != MAX_W){minque.push(Edge(i, j, _matrix[i][j]));}}}// 选出n-1条边int size = 0;W totalW = W();UnionFindSet ufs(n);while (!minque.empty()){Edge min = minque.top();minque.pop();if (!ufs.InSet(min._srci, min._dsti)){cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;minTree._AddEdge(min._srci, min._dsti, min._w);ufs.Union(min._srci, min._dsti);++size;totalW += min._w;}else{cout << "构成环:";cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;}}if (size == n - 1){return totalW;}else{return W();}}

Prim算法

如何理解Prim算法?

        Prim算法是一种用于求解加权连通无向图中最小生成树的算法。它的核心思想是从一个顶点开始,逐步扩展已选取的顶点集合,直到所有顶点都被包含在内。(使用的就是局部的贪心策略)具体步骤如下:

  1. 选择起始点:从图中任意选择一个顶点作为起始点。
  2. 构建边界集:将起始点加入已选取的顶点集合中,同时维护一个边界顶点集合,这些顶点是已选取顶点集合与未选取顶点集合之间的边界。
  3. 选择轻量级边:在边界集合中选择权值最小的边,将该边以及其连接的未选取顶点加入到已选取集合中。
  4. 更新边界集:更新边界集合,将新加入的顶点作为新的边界顶点。
  5. 重复直至完成:重复步骤3和步骤4,直到所有顶点都被加入到已选取的顶点集合中,此时形成的树即为最小生成树。

        总的来说,Prim算法的关键在于每一步都选择当前最优的选择,即权值最小的边,以此来保证最终得到的是最小生成树。这种贪心策略确保了算法的效率和结果的最优性。大致图解如下:

Prim算法的实现

        由于Prim是由局部逐步扩散到全局的,也就是说他并不会选择到选过的顶点,因此Prim的实现并不需要使用到并查集。对于上面Kruskal提到的Edge以及Self不多阐述。

        由于Prim需要指定一个顶点开始,因此接口相对Kruskal多传了一个顶点。接下来,按照上述的实现步骤一步一步实现:(1)初始化,同Kruskal一样,我们都是使用邻接矩阵来实现的,因此前期初始化是相同的操作,接着,获取对应的顶点下标映射,创建两个bool数组分别用于表示已选取的顶点集合和未选取顶点集合,true表示拥有该顶点,false表示不拥有。最后按照下标映射初始化两个数组,选取的则改为true,未选改为false,完成初始化。(2)使用优先级队列先将开始顶点的边放入队列中,同样设置size和totalW。(3)开始选边,出队列顶的边,通过判断是否在已选取的顶点集合来判断是否可选(可以变相理解为是否成环),如果不在表示为没有选过因此可以选,选取后将已选取的顶点集合和未选取顶点集合分别改变,再根据刚刚选取的点选取他临界的顶点。(4)在这个循环中,可能会出现很多重复的点的情况,但是我们有已选取的顶点集合来判断是否可选,这样不可选的会自动出队列而不被选取,最后根据size来判断是否可以形成最小生成树并且返回权值

        具体实现如下:

		W Prim(Self& minTree, const W& src){size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n);for (size_t i = 0; i < n; ++i){minTree._matrix[i].resize(n, MAX_W);}/*set<int> X;set<int> Y;X.insert(srci);for (size_t i = 0; i < n; ++i){if (i != srci){Y.insert(i);}}*/vector<bool> X(n, false);vector<bool> Y(n, true);X[srci] = true;Y[srci] = false;// 从X->Y集合中连接的边里面选出最小的边priority_queue<Edge, vector<Edge>, greater<Edge>> minq;// 先把srci连接的边添加到队列中for (size_t i = 0; i < n; ++i){if (_matrix[srci][i] != MAX_W){minq.push(Edge(srci, i, _matrix[srci][i]));}}cout << "Prim开始选边" << endl;size_t size = 0;W totalW = W();while (!minq.empty()){Edge min = minq.top();minq.pop();// 最小边的目标点也在X集合,则构成环if (X[min._dsti]){//cout << "构成环:";//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;}else{minTree._AddEdge(min._srci, min._dsti, min._w);//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;X[min._dsti] = true;Y[min._dsti] = false;++size;totalW += min._w;if (size == n - 1)break;for (size_t i = 0; i < n; ++i){if (_matrix[min._dsti][i] != MAX_W && Y[i]){minq.push(Edge(min._dsti, i, _matrix[min._dsti][i]));}}}}if (size == n - 1){return totalW;}else{return W();}}

 


                        感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

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

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

相关文章

Prometheus+Grafana 监控Tongweb7(by lqw)

文章目录 1.准备工作2.Tongweb7部署3.Prometheus部署4.上传jar包并配置Tongweb75.Prometheus配置6.安装和配置Grafana 1.准备工作 本次参考&#xff1a;Prometheus监控Tongweb容器 1.使用虚拟机ip&#xff1a;192.168.10.51&#xff08;tongweb&#xff09;&#xff0c;192.1…

vue iframe实现父页面实时调用子页面方法和内容

父页面标签添加鼠标按下事件 父页方法中建立iframe通信 实时调用子页面方法 实时更改子页面文本内容

Ubuntu上搭建TFTP服务

Ubuntu上搭建TFTP服务 TFTP服务简介搭建TFTP服务安装TFTP服务修改配置文件 重启服务 TFTP服务简介 TFTP是一个基于UDP协议实现的用于在客户机和服务器之间进行简单文件传输的协议&#xff0c;适用于开销不大、不复杂的应用场合。TFTP协议专门为小文件传输而设计&#xff0c;只…

[C语言]——操作符详解

目录 一.操作符的分类 二.二进制和进制转换 1.二进制转十进制 2.二进制转八进制和十六进制 2.1二进制转八进制 2.2二进制转十六进制 三.原码、反码、补码 四.移位操作符 1.左移操作符 2.右移操作符 五.位操作符&#xff1a;&、|、^、~ 练习1&#xff1a;编写代码实…

腾讯云GPU云服务器简介_GPU服务器购买指南_GPU云服务器操作

腾讯云GPU服务器是提供GPU算力的弹性计算服务&#xff0c;腾讯云GPU服务器具有超强的并行计算能力&#xff0c;可用于深度学习训练、科学计算、图形图像处理、视频编解码等场景&#xff0c;腾讯云百科txybk.com整理腾讯云GPU服务器租用价格表、GPU实例优势、GPU解决方案、GPU软…

Spring Boot Actuator介绍

大家在yaml中经常见到的这个配置 management: endpoints: web: exposure: #该配置线上需要去掉&#xff0c;会有未授权访问漏洞 include: "*" 他就是Actuator&#xff01; 一、什么是 Actuator Spring Boot Actuator 模块提供了生产级别…

漏洞挖掘技术综述与人工智能应用探索:从静态分析到深度学习,跨项目挑战与未来机遇

在网络安全和软件工程领域中&#xff0c;将机器学习应用于源代码漏洞挖掘是一种先进的自动化方法。该过程遵循典型的监督学习框架&#xff0c;并可细化为以下几个关键步骤&#xff1a; 数据预处理&#xff1a; 源代码解析与清理&#xff1a;首先对源代码进行文本解析&#xff…

zookeeper底层细节

zk 临时节点和watch机制实现注册中心自动注册和发现&#xff0c;数据都在内存&#xff0c;nio 多线程模型&#xff1b; cp注重一致性&#xff0c;数据不一致时集群不可用 事务请求处理方式 1.all事务由唯一服务器处理 2.将客户端事务请求转成proposal分发follower 3.等待半…

异常 --java学习笔记

异常 异常就是代表程序出现的问题 当一个方法出现问题&#xff0c;这个方法内部就会把这个问题的信息封装成一个异常对象&#xff0c;然后把这个异常对象抛给jvm虚拟机&#xff0c;jvm收到之后会先把出问题的程序先停下来&#xff0c;然后再把这个异常对象打印出来&#xff0c…

python之jsonpath的使用

文章目录 介绍安装语法语法规则举例说明 在 python 中使用获取所有结构所有子节点的作者获取所有子孙节点获取所有价格取出第三本书的所有信息取出价格大于70块的所有书本从mongodb 中取数据的示例 介绍 JSONPath能在复杂的JSON数据中 查找和提取所需的信息&#xff0c;它是一…

JavaMySQL高级一(下)

目录 1.常用函数 1.字符串函数 2.时间日期函数 3.聚合函数 4.数学函数 2.分布查询 3.子查询基础 1.简单子查询 1.常用函数 在程序开发过程中&#xff0c;除了简单的数据查询&#xff0c;还有基于已数据进行数据的统计分析计算等需求。因此&#xff0c;在SQL中将一…

电感回路中的电阻突然变大会怎样

大家好&#xff0c;我是砖一。 我们经常会看到这样一个电路&#xff0c;电感和电阻搭配在一块&#xff0c;加上5v供电&#xff0c;如果电感回路中的电阻突然变大会怎样&#xff1f; 假设电感L1100uH&#xff0c;电阻R110Ω。 图1 串入电感图 1&#xff0c;由开关状态分析电路…

【C语言】基本语法知识C语言函数操作符详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.基本语法 1.1 代码解释 1.1.1 main()主函数 1.1.2 int 1.1.3 { } 1.1.4 printf()库函数 1.1.5 stdio.h头文件 1.2 C语言的…

#LT8713SX适用于Type-C/DP1.4转三路Type-C/DP1.4/HDMI2.0应用方案,分辨率高达4K60HZ,支持SST/MST功能。

1. 描述 LT8713SX是一款高性能Type-C/DP1.4转Type-C/DP1.4/HD-DVI2.0转换器&#xff0c;具有三个可配置的DP1.4/HD-DVI2.0/DP输出接口和音频输出接口。LT8713SX 支持 DisplayPort™ 单流传输 &#xff08;SST&#xff09; 模式和多流传输 &#xff08;MST&#xff09; 模式。当…

韵达国际邀您参观2024杭州快递物流供应链与技术装备展览会

2024年7月8-10日 | 杭州国际博览中心 同期举办&#xff1a;数字物流技术展 新能源商用车及物流车展 电商物流包装展 冷链物流展 展会介绍 2024长三角快递物流供应链与技术装备展览会&#xff08;杭州&#xff09;&#xff0c;于2024年7月8-10日在杭州国际博览中心召开&#…

LeetCode每日一题【206. 反转链表】

思路&#xff1a;双指针&#xff0c;一前一后&#xff0c;逐个把指向后面的指针指向前面。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), ne…

Unity Mesh简化为Cube mesh

Mesh简化为Cube mesh &#x1f373;食用&#x1f959;子物体独立生成CubeMesh&#x1f96a;合并成一个CubeMesh&#x1f32d;Demo &#x1f373;食用 下载并导入插件&#x1f448;即可在代码中调用。 &#x1f959;子物体独立生成CubeMesh gameObject.ToCubeMesh_Invidual()…

109 项目整合 spring-quartz 启动自动执行定时任务

前言 项目中使用了 quartz 来支持定时任务的相关基础支撑, 但是 最近添加了一个 资源消耗比较高的定时任务, 发布到测试环境之后, 发现服务突然 起不起来了[资源比较有限] 然后 查看了一下日志, 这个定时任务怎么在执行?, 不是 配置的是 凌晨两点么, 然后 仔细一看 几乎配置…

jscpd检测代码的重复率

官方文档&#xff1a;jscpd jscpd 是一个开源的代码重复检测工具&#xff0c;它用于查找代码中的重复部分并生成相应的报告 1、比较两个目录之间的差异 yarn jscpd --skipLocal sre/test1/** sre/test2 --reporters html生成报告文档在 ./report/html 下面&#xff0c;可以打开…

linux下用docker安装mysql及导入文件

目录 1. 非root用户设置docker权限2. user账号安装mysql2. root账号打开防火墙3. 启动mysql容器3.1 在指定工作目录下建立文件夹3.2 配置文件3.3 开启mysql容器 4. 进入容器4.1 通过容器进入mysql4.1 设置账号4.2 建立数据库4.3 导入文件 5. windows连接数据库参考文件 1. 非ro…