最小生成树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.mzph.cn/news/69026.shtml

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

相关文章

Pytorch 的基本概念和使用场景介绍

文章目录 一、基本概念1. 张量&#xff08;Tensor&#xff09;2. 自动微分&#xff08;Autograd&#xff09;3. 计算图&#xff08;Computation Graph&#xff09;4. 动态计算图&#xff08;Dynamic Computation Graph&#xff09;5. 变量&#xff08;Variable&#xff09; 二、…

【用unity实现100个游戏之8】用Unity制作一个炸弹人游戏

文章目录 前言素材开始一、绘制地图二、玩家设置三、玩家移动四、玩家四方向动画运动切换 五、放置炸弹六、生成爆炸效果七、墙壁和可破坏障碍物的判断八、道具生成和效果九、玩家死亡十、简单的敌人AI十一、简单敌人AI十二、随机绘制地图十三、虚拟摇杆 最终效果待续源码完结 …

2023年行研行业研究报告

第一章 行业概述 1.1 行研行业 行业定义为同一类别的经济活动&#xff0c;这涉及生产相似产品、应用相同生产工艺或提供同类服务的集合&#xff0c;如食品饮料行业、服饰行业、机械制造行业、金融服务行业和移动互联网行业等。 为满足全球金融业的需求&#xff0c;1999年8月…

Linux之autofs自动挂载服务

目录 Linux之autofs自动挂载服务 产生原因 安装 配置文件分析 文件路径 作用 etc/auto.master文件内容格式 挂载参数 案例 案例1 --- 服务器创建共享目录&#xff0c;客户端实现自动挂载 案例2 --- 自动挂载光盘 Linux之autofs自动挂载服务 产生原因 在一般NFS文件系…

$attrs,$listeners

vue实现组件通信的方式有&#xff1a; 父子通信 父组件向子组件传递通过props定义各个属性来传递&#xff0c;子组件向父组件传递通过$emit触发事件 ref也可以访问组件实例跨级通信 vuex bus provide / inject $attrs / $listeners解释 $attrs / $listeners $attrs 将父组件中…

服务端请求伪造(SSRF)及漏洞复现

文章目录 渗透测试漏洞原理服务端请求伪造1. SSRF 概述1.1 SSRF 场景1.1.1 PHP 实现 1.2 SSRF 原理1.3 SSRF 危害 2. SSRF 攻防2.1 SSRF 利用2.1.1 文件访问2.1.2 端口扫描2.1.3 读取本地文件2.1.4 内网应用指纹识别2.1.5 攻击内网Web应用 2.2 SSRF 经典案例2.2.1 访问页面2.2.…

自然语言处理实战项目17-基于多种NLP模型的诈骗电话识别方法研究与应用实战

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目17-基于NLP模型的诈骗电话识别方法研究与应用&#xff0c;相信最近小伙伴都都看过《孤注一掷》这部写实的诈骗电影吧&#xff0c;电影主要围绕跨境网络诈骗展开&#xff0c;电影取材自上万起真…

基于Java+SpringBoot+Vue前后端分离善筹网(众筹)设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

vue3在路由route.js中获取不到仓库pinia中store里面的值

原因&#xff1a;小仓库(useUserStore )必须有大仓库(pinia)才能运行&#xff0c;在组件中能使用pinia仓库的数据&#xff0c;是因为在main.ts中已经在vue上面挂载了大仓库(pinia)&#xff0c;但是route.js不是vue组件&#xff0c;没有被挂载大仓库&#xff0c;所以不能运行 解…

使用(七牛云)为例子实现将文件上传到云服务器

目的 目前&#xff0c;用户的头像、分享生成的长图等文件都是存放在本地的&#xff0c;我们可以将他们存放在云服务器中&#xff0c;此处我们使用七牛云作为例子示范。 七牛云 创建账户并申请如下的两个bucket&#xff0c;分别是用户头像的存储空间和分享长图的存储空间。 …

数据库设计DDL

DDL&#xff1a;数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表&#xff09; DDL&#xff08;数据库操作&#xff09; 查询&#xff1a; 查询所有数据库&#xff1a;show databases; 查询当前数据库&#xff1a;select database(); 使用&#xff1a; 使用…

Python 之 match 表达式

Python 从 3.10 版本开始增加了 match 语句&#xff0c;和其他语言常见的 switch 语句极其相似&#xff0c;但功能更加强大。 本文通过实例&#xff0c;了解下其用法。 基本的 match 语句 def http_code(status): match status: case 400 | 404 | 418: …

java八股文面试[JVM]——JVM性能优化

JVM性能优化指南 JVM常用命令 jps 查看java进程 The jps command lists the instrumented Java HotSpot VMs on the target system. The command is limited to reporting information on JVMs for which it has the access permissions. jinfo &#xff08;1&#xff09;实时…

AIGC专栏3——Stable Diffusion结构解析-以图像生成图像(图生图,img2img)为例

AIGC专栏3——Stable Diffusion结构解析-以图像生成图像&#xff08;图生图&#xff0c;img2img&#xff09;为例 学习前言源码下载地址网络构建一、什么是Stable Diffusion&#xff08;SD&#xff09;二、Stable Diffusion的组成三、img2img生成流程1、输入图片编码2、文本编码…

SpringCloud(35):Nacos 服务发现快速入门

本小节,我们将演示如何使用Spring Cloud Alibaba Nacos Discovery为Spring cloud 应用程序与 Nacos 的无缝集成。 通过一些原生的spring cloud注解,我们可以快速来实现Spring cloud微服务的服务发现机制,并使用Nacos Server作为服务发现中心,统一管理所有微服务。 1 Spring…

vue3中TCplayer应用

环境win10:vitevue3elementUI 1 安装 npm install tcplayer.js2 使用 <template><div><video id"player-container-id" width"414" height"270" preload"auto" playsinline webkit-playsinline></video>&l…

联发科MTK6762/MT6762核心板_安卓主板小尺寸低功耗4G智能模块

MT6762安卓核心板是一款基于MTK平台的高性能智能模块&#xff0c;是一款工业级的产品。该芯片也被称为Helio P22。这款芯片内置了Arm Cortex-A53 CPU&#xff0c;最高可运行于2.0GHz。同时&#xff0c;它还提供灵活的LPDDR3/LPDDR4x内存控制器&#xff0c;此外&#xff0c;Medi…

【FreeRTOS】【应用篇】消息队列【下篇】

前言 本篇文章主要对 FreeRTOS 中消息队列的概念和相关函数进行了详解消息队列【下篇】详细剖析了消息队列中发送、接收时队列消息控制块中各种指针的行为&#xff0c;以及几个发送消息和接收消息的函数的运作流程笔者有关于 【FreeRTOS】【应用篇】消息队列【上篇】——队列基…

【链表OJ 11】复制带随机指针的链表

前言: &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&#x1f4a5; ✨✨刷题专栏:http://t.csdn.cn/UlvTc ⛳⛳本篇内容:力扣上链表OJ题目 目录 leetcode138. 复制带随机指针的链表 1. 问题描述 2.代码思路: 2.1拷贝节点插入到…

【文心一言大模型插件制作初体验】制作面试错题本大模型插件

文心一言插件开发初体验 效果图 注意&#xff1a;目前插件仅支持在本地运行&#xff0c;虽然只能自用&#xff0c;但仍然是一个不错的选择。&#xff08;什么&#xff1f;你说没有用&#xff1f;这不可能&#xff01;文心一言app可以支持语音&#xff0c;网页端结合手机端就可…