【数据结构】图的最小生成树

最小生成树

一个图中有N个顶点,边的数量一定是>=N-1,我们从中选取N-1条边,用来连接N个点,所形成的边权之和最小,就是最小生成树。

构成最小生成树的准则

  1. 只能使用图中的边来构造最小生成树
  2. 只能使用恰好n-1条边来连接图中的n个顶点
  3. 选用的n-1条边不能构成回路

构成最小生成树的的方法:Kruskal(克鲁斯卡尔)算法和Prim(普里姆)算法

二者都是基于逐步贪心的算法

Kruskal算法

Kruskal算法的思路

  1. 先构建出一个N个顶点的最小生成树图,其中不包含任何边,将原图的各个边按照权值进行排序
  2. 在排序的边中,选出一条边,如果这条边不会与其它边构成环,就添加到最小生成树图里
  3. 当选边数为N-1时,就构成最小生成树,否则不构成

描述这一过程,构成最小生成树

再选边时 都会构成环,此时已经是n-1条边,连接n个顶点,是最小生成树。

  • 要做选取最小的边,就需要将边的关系放到优先级队列中。每一个取边,就是top pop的过程。
  • 判断是否成环,需要一个集合,如果顶点A和B都在集合中,那么就构成环。
  • 只要优先级队列还有边就要一直判断选边,直到队列为空,如果最后选取了n-1条边,那么就是最小生成树,反之不是,并返回所有的权和。
		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;}bool operator<(const Edge& e) const{return _w < e._w;}};//最小生成树W Kruskal(Self& minTree){size_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._vIndexMap = _vIndexMap;minTree._matrix.resize(n);for (size_t i = 0; i < minTree._vertexs.size(); i++){minTree._matrix[i].resize(n, MAX_W);}//优先级队列priority_queue<Edge, vector<Edge>, greater<Edge>> minque;//把所有的边都放进去for (size_t i = 0; i < _matrix.size(); i++){for (size_t j = 0; j < _matrix[i].size(); j++){if (i<j&&_matrix[i][j] != MAX_W){minque.push(Edge(i, j, _matrix[i][j]));}}}//选边W total = W();size_t i = 1;UnionFindSet ufs(_vertexs.size());while (!minque.empty()){Edge minedge = minque.top();minque.pop();if (!ufs.InSet(minedge._srci,minedge._dsti)){cout << _vertexs[minedge._srci] << "->" << _vertexs[minedge._dsti] << ":" << minedge._w << endl;ufs.Union(minedge._srci, minedge._dsti);minTree._AddEdge(minedge._srci, minedge._srci, minedge._w);total += minedge._w;++i;}else{cout << "构成环" << endl;}}if (i == _vertexs.size()){return total;}return W();}
  1. 创建最小生成树的顶点和映射,提前为邻接矩阵开空间。
  2. 遍历原图的邻接矩阵,将边都放到优先级队列中。
  3. 选边时,只有不在同一个集合中,才被添加到最小生成树的边里。

对于自定义类型的优先级队列,需要自定比较函数。

这里也运用到并查集的知识。


Prim算法

算法的基本思路

  • 构造一个含 n 个顶点、不含任何边的图作为最小生成树,将图中的顶点分为两个集合,X Y 集合中的顶点是已经连接到最小生成树中的顶点,Y集合中的顶点是还没有连接到最小生成树中的顶点,刚开始时X 集合中只包含给定的起始顶点。
  • 每次从连接X 集合与 Y 集合的所有边中选出一条权值最小的边,将其加入到最小生成树中,由于选出来的边对应的两个顶点一个属于X 集合,另一个属于Y集合,因此是不会构成回路的。

步骤

  1. 先创建最小生成树的图,构造顶点和下标映射,为邻接矩阵开辟空间。
  2. 创建 X Y标记数组,X是已经包含的集合(全false),Y是没有被包含的集合(true)。
  3. 求出srci表示从哪一个顶点开始。X[srci]=true,Y[srci]=false
  4. 将srci所有相邻的边都放到优先级队列中,遍历优先级队列,如果不构成环,就添加边
  5. 然后将dsti的边相邻也添加到队列中
  6. 确保不相邻的方法:srci在X中,dsti在Y中,就是不相邻
		W Prim(Self& minTree, const W& src){size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._vIndexMap = _vIndexMap;minTree._matrix.resize(n);for (size_t i = 0; i < minTree._vertexs.size(); i++){minTree._matrix[i].resize(n, MAX_W);}vector<bool> X(n, false);vector<bool> Y(n, true);X[srci] = true;Y[srci] = false;//优先级队列priority_queue<Edge, vector<Edge>, greater<Edge>> minque;//把所有的边都放进去for (size_t i = 0; i < _matrix.size(); i++){if (  _matrix[srci][i] != MAX_W){minque.push(Edge(srci, i, _matrix[srci][i]));}}cout << "Prim 选边" << endl;W total = W();size_t i = 1;while (!minque.empty()){Edge minedge = minque.top();minque.pop();if (X[minedge._dsti]){//cout << "构成环";}else{minTree._AddEdge(minedge._srci, minedge._dsti, minedge._w);X[minedge._dsti] = true;Y[minedge._dsti] = false;++i;total += minedge._w;if (i == n)		break;//将dsti相邻的都放到队列中for (size_t index = 0; index < n; index++){if (Y[index]&&_matrix[minedge._dsti][index] != MAX_W){minque.push(Edge(minedge._dsti, index,_matrix[minedge._dsti][index]));}}}}if (i == n){return total;}return W();}

画图演示这个过程

........省略几步类似的步骤

选择 i-g  i-h a-h时都会成环,不操作

最终的结果

最后依旧需要判断,如果完成n-1次选边后,可以构成最小生成树

否则,无法构成


Prim算法每次选边都会遍历相邻的边,是时间复杂度较大的算法。

Kruskal是全局贪心,每次选边都是选择最小的边。

Prim算法是局部贪心,总是选择目前相连的最小边。

二者所得到的权值是一样的。

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

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

相关文章

Stable Diffusion 绘画入门教程(webui)-提示词

通过上一篇文章大家应该已经掌握了sd的使用流程&#xff0c;本篇文章重点讲一下提示词应该如何写 AI绘画本身就是通过我们写一些提示词&#xff0c;然后生成对应的画面&#xff0c;所以提示词的重要性不言而喻。 要想生成更加符合自己脑海里画面的图片&#xff0c;就尽量按照…

术业有专攻!三防加固平板助力工业起飞

在日常使用中的商业电脑比较追求时效性&#xff0c;以市场定位做标准&#xff0c;内部元件只需满足一般要求就行&#xff0c;使用寿命比较短。而三防平板电脑是主要运用在复杂、恶劣的环境下所以在需求方面较高,需要保证产品在恶劣条件下正常使用&#xff0c;满足行业领域的需求…

【CCEdit】通过扩散模型进行创意且可控的视频编辑

文章目录 CCEdit1. 核心特性1.1 三叉戟网络结构1.2 精细的外观控制1.3 高度的自适应性 2. 三叉戟结构2.1 结构分支&#xff08;ControlNet架构&#xff09;2.2 外观分支2.3 主分支 3. 数据集——BalanceCC benchmark dataset4. 训练5. 长视频编辑6. 使用场景7. 评估指标 CCEdit…

单片机01天---stm32基本信息了解

下载数据手册 以STM32F407ZG为例 网站&#xff1a;www.st.com&#xff0c;搜索芯片型号&#xff0c;下载“数据手册”使用 数据手册使用 查看芯片型号信息 芯片资源信息 时钟框图 芯片资源表格下方 GPIO口表格 一般位于下图后面的位置 ①工作电压&#xff1a;1.8V – 3.6V…

Idea启动Gradle报错: Please, re-import the Gradle project and try again

Idea启动Gradle报错&#xff1a;Warning:Unable to make the module: reading, related gradle configuration was not found. Please, re-import the Gradle project and try again. 解决办法&#xff1a; 开启步骤&#xff1a;View -> Tool Windows -> Gradle 点击refe…

HN 千赞热贴|创业 4 年,那些狠狠打我脸的技术选型

Hacker News 帖子 过年这段时间&#xff0c;Hacker News 上也涌现了不少好帖子&#xff0c;除了霸榜的 Sora 外&#xff0c;技术贴最靠前的就是这篇 (Almost) Every infrastructure decision I endorse or regret after 4 years running infrastructure at a startup。作者根据…

【Django开发】0到1开发美多shop项目:图形和短信验证码。全md文档笔记(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现前后端…

【Java多线程】线程安全问题与解决方案

目录 1、线程安全问题 1.2、线程安全原因 2、线程加锁 2.1、synchronized 关键字 2.2、完善代码 2.3、对同一个线程的加锁操作 3、内容补充 3.1、内存可见性问题 3.2、指令重排序问题 3.3、解决方法 3.4、总结 volatile 关键字 1、线程安全问题 某个代码&#xff…

初识结构体(C语言)

目录 1、结构体声明 2、结构体访问 3、结构体传参 1、结构体声明 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每一个成员可以是不同类型的变量。有点像数组&#xff0c;但是一个数组只能存放同一种类型的变量。如果要描述复杂对象的时候&#xff0c;对象由多…

基于Java SSM框架实现留学生交流互动论坛网站项目【项目源码+论文说明】

摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

基于ant的图片上传组件封装(复制即可使用)

/*** 上传图片组件* param imgSize 图片大小限制* param data 上传数据* param disabled 是否禁用*/import React, { useState,useEffect } from react; import { Upload, Icon, message} from antd; const UploadImage ({imgSize 50,data { Directory: Image },disabled f…

Vue封装全局公共方法

有的时候,我们需要在多个组件里调用一个公共方法,这样我们就能将这个方法封装成全局的公共方法。 我们先在src下的assets里新建一个js文件夹,然后建一个common.js的文件,如下图所示: 然后在common.js里写我们的公共方法,比如这里我们写了一个testLink的方法,然后在main…

Apache Flink连载(三十):Flink 内存模型

🏡 个人主页:IT贫道-CSDN博客 🚩 私聊博主:私聊博主加WX好友,获取更多资料哦~ 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录

【GUI编程】Tkinter之OptionMenu

OptionMenu OptionMenu类是一个辅助类&#xff0c;它用来创建弹出菜单&#xff0c;并且有一恶搞按钮显示它。它非常类似Windows上的下拉列表插件。 如果要获取当前选项菜单的值&#xff0c;你需要把它和一个Tkinter变量联系起来。 def __init__(self, master, variable, val…

“无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【上】

"无限交互&#xff0c;全新驾驶体验&#xff01;智能语音小车&#xff0c;与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【上】 前言预备知识1. L9110S电机控制器接线1.1 L9110S概述1.2 L9110S IO口描述1.3 L9110S 实物图1.4 L9110S与单片机接线 2. L9110前…

PostgreSQL按日期列创建分区表

在PostgreSQL中&#xff0c;实现自动创建分区表主要依赖于表的分区功能&#xff0c;这一功能从PostgreSQL 10开始引入。分区表可以帮助管理大量数据&#xff0c;通过分布数据到不同的分区来提高查询效率和数据维护的便捷性。以下是在PostgreSQL中自动创建分区表的一般步骤&…

【Git】Gitbash使用ssh 上传本地项目到github

SSH Git上传项目到GitHub&#xff08;图文&#xff09;_git ssh上传github-CSDN博客 前提 ssh-keygen -t rsa -C “自己的github电子邮箱” 生成密钥&#xff0c;公钥保存到自己的github的ssh里 1.先创建一个仓库&#xff0c;复制ssh地址 git init git add . git commit -m …

GEE必须会教程—跳舞的线(字符串类型)

字符串&#xff0c;GEE上跳舞的线&#xff01; GEE学习之路漫长&#xff0c;跟着小编一起走进今天的数据类型的学习。字符串是各大编程语言的常用数据类型&#xff0c;我们今天需要了解GEE平台上字符串的定义、以及常用的方法。 1.定义字符串 //字符串构造 var base_str &q…

「Java同步原理与底层实现解析」

原理概要&#xff1a; java虚拟机中的同步基于进入与结束Monitor对象实现&#xff0c;无论是显式同步&#xff08;同步代码块进入在jvm是根据monitorenter标志、结束是monitorexit标志&#xff0c;那最后一个是monitorexit是异常结束时被执行的释放指令&#xff09;、隐式同步…

STM32 输入捕获模式测频率

单片机学习&#xff01; 目录 文章目录 前言 一、输入捕获测频率配置步骤 二、代码示例及注意事项 2.1 RCC开启时钟 2.2 GPIO初始化 2.3 配置时基单元 2.4 配置输入捕获单元 2.5 选择从模式的触发源 2.6 配置从模式为Reset 2.7 开启定时器 总结 前言 博文介绍如何配置输入捕获电…