数据结构-图-最小生成树问题
最小生成树
- 并查集
- 定义
- 举例说明
- 查找某个元素属于哪个集合
- 代码实现
- 路径压缩
- Kruskal
- 算法原理
- 代码实现
- Prim
- 算法原理
- 代码实现
并查集
定义
🚀在一些应用问题中,需要将n个不同的元素分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中,要反复用到查询某一个元素属于哪个集合的运算。适合描述这类问题的抽象数据结构叫做并查集。
🚀由于每个集合就是一颗树形结构,一个并查集内存在多个集合,所以并查集是一个森林。
🚀通常用数组来充当这种数据结构,采用双亲表示法的方式,即子节点存储父节点的指针。
举例说明
🚀例如,有10名同学,它们的编号分别为0-9,它们来自三个班级(1班,2班,3班),其中0,3,4号同学来自一班,1,7,9同学来自2班,2,5,6,8号同学来自3班。
初始结构:每个元素各自独立为一个集合。
元素的合并:相同班级的同学合并为同一个集合。
🚀合并规则:
将子节点的数据加到父节点的数据中,将字节点的数据改为父节点的指针。这样父节点中的数据的绝对值就是此集合中元素个数,各个子节点存储的都是指向父节点的指针。
🚀合并的优化:
在合并时,最好将集合内元素数量较少的集合合并到集合内元素数量多的集合。这样在查找某个元素属于哪个集合时可以减少时间消耗。例如,将1班和3班合并,优先是将1班合并到3班,这样第三层的结点数量比较少,相比于将3班合并到1班。
查找某个元素属于哪个集合
🚀例如查找n号下元素属于哪个集合,只需要判断n号位置的元素是否为负数,如果为负数说明n号位置就是这个集合的根节点(用根节点的下标来标识集合),如果不是负数就顺着父节点向上访问,直到根节点为止。
代码实现
#pragma once#include <vector>
#include <iostream>/*
* 并查集
* 1.核心采用双亲表示法,孩子结点存储的是父节点的下表
* 2.采用类似堆的结构表示---在数组中存储
*/
class UnionFindSet {
private:std::vector<int> _ufs;
public:UnionFindSet(size_t n) :_ufs(n,-1) {}int FindRoot(int x) {int parent = x;//一直向上找到存储负数的结点while (_ufs[parent] >= 0) {parent = _ufs[parent];}return parent;}void Union(int x1, int x2) {int root1 = FindRoot(x1);int root2 = FindRoot(x2);if (root1 == root2) { return; } //本身就在一个集合当中if (abs(_ufs[root1]) < abs(_ufs[root2])) {std::swap(root1, root2);}_ufs[root1] += _ufs[root2]; //将root2结点的数据加到root1结点的数据上_ufs[root2] = root1; //将root2结点的数据修改为root1(根结点的下标)}size_t SetSize() const {size_t cnt = 0;for (auto& e : _ufs) {if (e < 0) { cnt++; } //结点数据为0的就表示为根结点,能够代表一个集合}return cnt;}bool InSet(int x1, int x2) {int root1 = FindRoot(x1);int root2 = FindRoot(x2);return root1 == root2;}
};
路径压缩
🚀在经过多次集合合并时,可能会出现某个集合的树形结构深度很深,从而导致查询某个元素的集合时时间消耗比较大,所以可以对树形结构的路径进行压缩。
上图只是个示例,通常来说数据量很大的时候才需要路径压缩。
压缩策略:
在查找某个元素属于哪个集合时进行路径的压缩,在找到某个元素的根节点后不着急返回,而是依次将此路径上的该元素的父节点合并到根结点处,完成路径压缩。这样下次再查找某个元素属于哪个集合时,就可以提高效率。
int FindRoot(int x) {int parent = x;//一直向上找到存储负数的结点while (_ufs[parent] >= 0) {parent = _ufs[parent];}//核心代码int cur = x;while (_ufs[cur] >= 0) {int tmp = cur;cur = _ufs[cur];_ufs[tmp] = parent;}return parent;
}
Kruskal
最小生成树
🚀最小生成树就是在图的所有生成树中找出各个路径权值和最小的那个路径。
算法原理
🚀克鲁斯卡尔算法是求图的最小生成树的一个经典算法,其采用全局贪心的思想,每次都去选权值最小的边(可以将所有边放在一个小根堆中每次获取堆顶元素)去构成最小生成树(不能重复选取某一个边),但是这种贪心方式可能会造成环路的产生,所以在每选一个边之前都要判断一下选取这个边后会不会构成环路。而判环的这个步骤使用并查集是非常适合的,可以将已经选取出来的构成最小生成树的顶点放在一个集合S中,在添加某个边时,如果这个边的两个顶点均位于集合S中,说明构成了环路这条边是不能选取的。
选取边的过程:
🚀最小生成树的结果并不唯一,例如上图中有两个权值为8的边,上面的选法中是选取了ah这条边,选取bc这条边也是没有问题的。
代码实现
struct Edge {size_t _srci;size_t _dsti;W _weight;Edge(size_t srci,size_t dsti,const W& weight) :_srci(srci),_dsti(dsti),_weight(weight) {}bool operator > (const Edge& e) const {return _weight > e._weight;}
};W Kruskal(self& min_tree) {size_t n = _vertex.size();min_tree._vertex = _vertex;min_tree._index_map = _index_map;//初始化矩阵min_tree._matrix.resize(n, std::vector<W>(n, W_MAX));//开始计算最小生成树std::priority_queue<Edge,std::vector<Edge>,std::greater<Edge>> pq;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (i < j && _matrix[i][j] != W_MAX) { //i<j在无向图中避免同一条边添加两次pq.push(Edge(i, j, _matrix[i][j]));}}}W total = W();//记录权值和size_t size = 0;//记录挑选出的边的个数UnionFindSet ufs(n);while (size < n - 1 && !pq.empty()) {Edge eg = pq.top();pq.pop();if (ufs.InSet(eg._srci, eg._dsti) == false) {min_tree._AddEdge(eg._srci, eg._dsti, eg._weight);ufs.Union((int)eg._srci, (int)eg._dsti);total += eg._weight;++size;}}if (size != n - 1) {return W();}return total;
}
Prim
算法原理
🚀Prim算法与Krunskal算法都是采用贪心的策略,但是Prim算法采用的是局部贪心的策略,在Prim算法中会将图的顶点分类到两个集合X,Y中,对于已经选入到最小生成树的边相连的顶点放在X集合中,没有选进最小生成树的顶点放入Y集合。而在贪心选取权值最小的边时,是在一个顶点位于X集合另一个顶点位于Y集合的所有边中去选择的。所以Prim算法需要指定一个起始顶点。当某个顶点被添加到X集合后,要将与其相连的边放入到优先级队列中,放入优先级队列的边也是有要求的就是它另一个顶点必须是在Y集合中的(如果采用优先级队列的方式存储边,仍然会出现环路情况,所以在选边的同时也要注意判环操作)
选取边的过程
假设起始顶点为a:
代码实现
W Prim(self& min_tree, const V& src) {size_t n = _vertex.size();min_tree._vertex = _vertex;min_tree._index_map = _index_map;//初始化矩阵min_tree._matrix.resize(n, std::vector<W>(n, W_MAX));//将所有的顶点划分为两个集合 X Y,已经选择的点放入X集合中,没有选择的点在Y集合中size_t srci = this->GetVertexIndex(src);std::vector<bool> X(n, false);std::vector<bool> Y(n, true);X[srci] = true;Y[srci] = false;//将与X集合中所有顶点相连的边存入优先级队列,以供贪心选择std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> pq;for (size_t i = 0; i < n; ++i) {if (_matrix[srci][i] != W_MAX) {pq.push(Edge(srci, i, _matrix[srci][i]));}}size_t size = 0;W total_w = W();while (size < n - 1 && !pq.empty()) {Edge eg = pq.top();pq.pop();//防止选出的边构成环if (true == X[eg._dsti]) {continue;}X[eg._dsti] = true;Y[eg._dsti] = false;min_tree._AddEdge(eg._srci, eg._dsti, eg._weight);++size;total_w += eg._weight;//std::cout << _vertex[eg._srci] << "->" << _vertex[eg._dsti] << ":" << eg._weight << std::endl;//将以dsti点为起点的边加入到队列中for (size_t i = 0; i < n; i++) {//存在边且终点位于Y集合中if (_matrix[eg._dsti][i] != W_MAX && Y[i]) {pq.push(Edge(eg._dsti, i, _matrix[eg._dsti][i]));}}}if (size != n - 1) {return W();}return total_w;
}
相关文章:
数据结构-图-最小生成树问题
最小生成树 并查集定义举例说明查找某个元素属于哪个集合代码实现路径压缩 Kruskal算法原理代码实现 Prim算法原理代码实现 并查集 定义 🚀在一些应用问题中,需要将n个不同的元素分成一些不相交的集合。开始时,每个元素自成一个单元素集合&…...
使用vite+npm封装组件库并发布到npm仓库
组件库背景:使用elementplusvue封装了一个通过表单组件。通过JSX对el-form下的el-input和el-button等表单进行统一封装,最后达到,通过数据即可一键生成页面表单的功能。 1.使用vite创建vue项目 npm create vitelatest elementplus-auto-form…...
85.最大矩形
单调栈,时间复杂度o(mn),空间复杂度o(mn) class Solution { public:int maximalRectangle(vector<vector<char>>& matrix) {int mmatrix.size();if(m0){return 0;}int nmatrix[0].size();//记录矩阵中每个元素左边连续1的数量vector<…...
Windows服务器 开机自启动服务
1、新建txt,并粘贴下面脚本 start cmd /k "cd /d D:\ahjd&&java -jar clips-admin.jar" start cmd /k "cd /d D:\ahjd\dist&&simple-http-server.exe -i -p 8000"说明,脚本格式为:start cmd /k “cd /d…...
《算法通关之路》chapter17一些通用解题模板
《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。 1 二分法 1.1 普通二分法 # 查找nums数组中元素值为target的下标。如果不存在,则返回-1def bs(nums: list[int], target: int) -> int :l, h …...
常用求解器安装
1 建模语言pyomo Pyomo是一个Python建模语言,用于数学优化建模。它可以与不同的求解器(如Gurobi,CPLEX,GLPK,SCIP等)集成使用,以求解各种数学优化问题。可以使用Pyomo建立数学优化模型…...
第三章:最新版零基础学习 PYTHON 教程(第一节 - Python 运算符)
在Python编程中,运算符一般用于对值和变量进行操作。这些是用于逻辑和算术运算的标准符号。在本文中,我们将研究不同类型的Python 运算符。 运算符:这些是特殊符号。例如- + 、 * 、 / 等。操作数:它是应用运算符的值。目录 Python 中的运算符类型 Python 中的算术运算符…...
细粒度特征提取和定位用于目标检测:PPCNN
1、简介 近年来,深度卷积神经网络在计算机视觉上取得了优异的性能。深度卷积神经网络以精确地分类目标信息而闻名,并采用了简单的卷积体系结构来降低图层的复杂性。基于深度卷积神经网络概念设计的VGG网络。VGGNet在对大规模图像进行分类方面取得了巨大…...
【STM32单片机】数学自动出题器设计
文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器,使用按键、IIC OLED模块等。 主要功能: 系统运行后,OLED液晶显示出题器开机界面,默认结果范围为100,可按…...
C语言之动态内存管理篇(1)
目录 为什么存在动态内存分配 动态内存函数的介绍 malloc free calloc realloc 常见的动态内存错误 今天收假了,抓紧时间写几篇博客。我又来赶进度了。今天我们来讲解动态内存管理。🆗🆗 为什么存在动态内存分配 假设我们去实现一个…...
React18入门(第二篇)——React18+Ts项目配置husky、eslint、pretttier、commitLint
前言 我的项目版本如下: React: V18.2.0Node.js: V16.14.0TypeScript:最新版工具: VsCode 本文将采用图文详解的方式,手把手带你快速完成在React项目中配置husky、prettier、commitLint,实现编码规范的统…...
【VINS】苹果手机采集单目相机+IMU数据离线运行VINS-Mono
0.准备工作 开个新坑,之前用Android手机做过离线采集数据的实验,这次用IPhone来测试! 1.虚拟机配置Mac OS 下载一个Mac OS 的ios镜像,打开虚拟机按照跟Ubuntu差不多的方式安装,但是发现没有Mac OS的入口。 因为VMwa…...
数据结构 2.1 单链表
1.单链表 线性表:1.有限的序列 2.序列中的每一个元素都有唯一的前驱和后继,除了开头和结尾的两个节点。 顺序表:分配一块连续的内存去存放这些元素,eg、数组 链表:内存是不连续的,元素会各自被分配一块内…...
[Machine Learning]pytorch手搓一个神经网络模型
因为之前虽然写过一点点关于pytorch的东西,但是用的还是他太少了。 这次从头开始,尝试着搓出一个神经网络模型 (因为没有什么训练数据,所以最后的训练部分使用可能不太好跑起来的代码作为演示,如果有需要自己连上数据…...
KdMapper扩展实现之Dell(pcdsrvc_x64.pkms)
1.背景 KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称pcds…...
python和go相互调用的两种方法
前言 Python 和 Go 语言是两种不同的编程语言,它们分别有自己的优势和适用场景。在一些项目中,由于团队内已有的技术栈或者某一部分业务的需求,可能需要 Python 和 Go 相互调用,以此来提升效率和性能。 性能优势 Go 通常比 Python 更高效&…...
c# 分部视图笔记
Html.Partial("**", 1) public ActionResult **(int page) { ViewBag.page page; return PartialView("**"); }...
Vue3最佳实践 第七章 TypeScript 中
Vue组件中TypeScript 在Vue组件中,我们可以使用TypeScript进行各种类型的设置,包括props、Reactive和ref等。下面,让我们详细地探讨一下这些设置。 设置描述设置props在Vue中,props本身就具有类型设定的功能。但如果你希望使用Ty…...
(三)行为模式:8、状态模式(State Pattern)(C++示例)
目录 1、状态模式(State Pattern)含义 2、状态模式的UML图学习 3、状态模式的应用场景 4、状态模式的优缺点 (1)优点 (2)缺点 5、C实现状态模式的实例 1、状态模式(State Pattern&#x…...
nginx的配置文件概述及简单demo(二)
默认配置文件 当安装完nginx后,它的目录下通常有默认的配置文件 #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connection…...
Apollo Planning2.0决策规划算法代码详细解析 (2): vscode gdb单步调试环境搭建
前言: apollo planning2.0 在新版本中在降低学习和二次开发成本上进行了一些重要的优化,重要的优化有接口优化、task插件化、配置参数改造等。 GNU symbolic debugger,简称「GDB 调试器」,是 Linux 平台下最常用的一款程序调试器。GDB 编译器通常以 gdb 命令的形式在终端…...
flex 布局:元素/文字靠右
前言 略 使用flex的justify-content属性控制元素的摆放位置 靠右 <view class"more">展开更多<text class"iconfont20231007 icon-zhankai"></text></view>.more {display: flex;flex-direction: row;color: #636363;justify-co…...
java基础-第1章-走进java世界
一、计算机基础知识 常用的DOS命令 二、计算机语言介绍 三、Java语言概述 四、Java环境的搭建 JDK安装图解 环境变量的配置 配置环境变量意义 配置环境变量步骤 五、第一个Java程序 编写Java源程序 编译Java源文件 运行Java程序 六、Java语言运行机制 核心机制—Java虚拟机 核…...
jvm 堆内存 栈内存 大小设置
4种方式配置不同作用域的jvm的堆栈内存。 1、Eclise 中设置jvm内存: 改动eclipse的配置文件,对全部project都起作用 改动eclipse根文件夹下的eclipse.ini文件 -vmargs //虚拟机设置 -Xms40m //初始内存 -Xmx256m //最大内存 -Xmn16m //最小内存 -XX:PermSize=128M //非堆内…...
免杀对抗-反沙盒+反调试
反VT-沙盒检测-Go&Python 介绍: 近年来,各类恶意软件层出不穷,反病毒软件也更新了各种检测方案以提高检率。 其中比较有效的方案是动态沙箱检测技术,即通过在沙箱中运行程序并观察程序行为来判断程序是否为恶意程序。简单来说…...
QTimer类的使用方法
本文介绍QTimer类的使用方法。 1.单次触发 在某些情况下,定时器只运行一次,可使用单次触发方式。 QTimer *timer new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::timeout); timer->setSingleShot(true); timer-…...
(三)行为模式:9、空对象模式(Null Object Pattern)(C++示例)
目录 1、空对象模式(Null Object Pattern)含义 2、空对象模式的主要涉及以下几个角色 3、空对象模式的应用场景 4、空对象模式的优缺点 (1)优点 (2)缺点 5、C实现空对象模式的实例 1、空对象模式&am…...
Django实战项目-学习任务系统-用户登录
第一步:先创建一个Django应用程序框架代码 1,先创建一个Django项目 django-admin startproject mysite将创建一个目录,其布局如下:mysite/manage.pymysite/__init__.pysettings.pyurls.pyasgi.pywsgi.py 2,再创建一个…...
【动手学深度学习-Pytorch版】Transformer代码总结
本文是纯纯的撸代码讲解,没有任何Transformer的基础内容~ 是从0榨干Transformer代码系列,借用的是李沐老师上课时讲解的代码。 本文是根据每个模块的实现过程来进行讲解的。如果您想获取关于Transformer具体的实现细节(不含代码)可…...
做外贸独立站选Shopify还是WordPress?
现在确实会有很多新人想做独立站,毕竟跨境电商平台内卷严重,平台规则限制不断升级,脱离平台“绑架”布局独立站,才能获得更多流量、订单、塑造品牌价值。然而,在选择建立外贸独立站的过程中,选择适合的建站…...
如何推广自己的公司官网/上海网络公司seo
线程基本方法一、线程等待(wait)二、线程睡眠(sleep)三、线程让步(yield)四、线程中断(interrupt)五、Join 等待其他线程终止六、为什么要用 join()方法?七、线程唤醒&am…...
濮阳网站优化公司哪家好/长沙网
下面我们来看看两个小demo实现定时任务的执行: 一、 package test2;import org.quartz.CronScheduleBuilder; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.…...
做电影网站算侵权吗/自助建站系统个人网站
1. Introduction大梵天创造世界的时候做了三根柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一…...
seo查询seo优化/长春seo代理
Css的布局格式一列式:一列式布局是最基本的布局方式,通过直接创建一个div就可以实现。图中有两种一列式的样式,分别是宽度自适应屏幕(这是默认的块级元素的属性);另外一种是依据文档内容来填充宽度…...
南通网站制作维护/互联网全网推广
类集中提供了以下几种接口: 1.单值操作接口:conllection,List,Set list和set是conllection接口的子接口 2.一对值的操作接口:Map 3.排序的操作接口:SortedMap, SortedSet 4.输出的接口:Iterator, ListIterator, Enumeration 5.队列…...
wordpress设置可写/seopc流量排行榜企业
计算机网络 练习(一百四十二) 某网络拓扑图如下所示,若采用 RIP 协议,在路由器 Router2 上需要进行 RIP 声明的网络是() A. 仅网络 1 B. 网络 1、202.117.112.0/30 和 202.117.113.0/30 C. 网络 1、网络…...