前言:Prim算法是图论中的算法,用来生成图的最小生成树。本篇文章介绍算法的流程,实现思想,和具体代码实现,使用c语言。学习需要输出才能理解的更透彻,所以说坚持写文章,希望可以用自己的方式把一些知识的原理描述出来。我会结合示意图清晰地展现出所有的流程,用人类语言把整个过程表述清楚,在写代码的时候才能做到胸有成竹,而不是模棱两可或者说差不多就是这样。本篇文章假设你已经具有了基本的数据结构和c语言的基础。
分享一个学习算法的神奇网站,可以可视化算法的执行过程。
Prim算法

一、Prim算法实现思路

Prim算法的作用是实现图的最小生成树。
在这里插入图片描述

Prim算法使用的是贪心算法的策略,每次都会找和当前的节点相连的边中,权值最小的节点。以上面这个图为例,首先我们要确定最小生成树的根节点,我们就设定为0节点。下面我们分步骤进行
① 根据贪心算法的原则,与0相连的下一个节点,我们要找0节点的边中,权值最小的边,0节点相连的一共有4条边,分别是

起点终点权值
015
021
035
047

找到权值最小的,就是连接2号节点的边,权值为1,所以会将2号节点加入到最小生成树中。
在这里插入图片描述
② 现在构成了图中所画的一棵生成树,需要将这两个节点看成一个整体,找和这棵生成树相连的边中,权值最小的边,所有的边:

起点终点权值
015
035
047
248
252
268

最小的边就是连接2号节点和五号节点的边,权值为2,然后就将5号节点加入到生成树中,并且5号节点连的是2号节点,就会生成下面这棵生成树
在这里插入图片描述

③接下来看和 1-2-5 生成树相连的节点的权值,所有的边:

起点终点权值
015
035
047
248
268
573
534

权值最小的边就是连接5号节点和3号节点的边,权值为3,那么就将3号节点加入到生成树中,生成树如下:
在这里插入图片描述
④ 再看和当前的生成树相连的所有边:

起点终点权值
015
035
047
248
268
534
748

权值最小的边是连接5号节点和3号节点的边,权值为4,构成的生成树如下:
在这里插入图片描述
⑤ 这里有一个需要注意的地方,就是最小生成树一定是不能有环,所以从3到0的边就不能再考虑了,和当前生成树相连的边:

起点终点权值
015
047
248
268
748
314

权值最小的边是连接3号节点和1号节点的边,权值为4,那么就将1号节点加入到生成树:
在这里插入图片描述
⑥ 去除会形成回路的边 0 -> 5, 和当前的生成树相连的所有的边:

起点终点权值
047
248
268
748
其中权值最小的边是 连接0号节点和4号节点的边,权值为7,将4号节点加入生成树,形成下面一个生成树:

在这里插入图片描述
⑦ 此时的边只有两条了,因为形成回路的边要去掉

起点终点权值
268
467
选择连接4号节点和6号节点的边,权值为7,至此所有的节点添加完毕

在这里插入图片描述

二、代码实现

通过上面的文字描述和图像展示,我们已经了解到了普利姆算法的执行的流程,接下来我们看一下代码层面的实现。分析一个问题,首先是站在人类思维像上面一样,写出步骤,然后就是需要转化成代码。这个过程还是挺复杂的,比较抽象。
一个功能的代码实现大概可以分为下面:
1、数据的类型、常量,需要定义结构体和常量,常量如最大值的限定等
2、入参
3、主函数
4、初始化辅助变量,需要分析都需要什么辅助的变量
5、流程执行
7、输出结果,printf或者return
当然关键就是第五步,其实前置的步骤都是为第五步服务的。我们先分析一下主流程需要做什么:
1、找和当前生成树相连的边中权值最小的边
2、将边加入当前生成树
3、需要记录当前的节点相连的在生成树中的节点,才能正确绘制树
那么我们就需要一个数组,来存储和当前生成树直接相连的所有的边的权值,其实就是节点到生成树的距离,如果节点在生成树内部,那么距离就是0,如果没有直接相连,那么距离就是正无穷,这个数组的名字取做 lowcost,意为最低成本的构成树,这个数组的初始化需要通过遍历邻接数组获取,我们都假设树的根节点为0号节点,那么lowcost 初始时就是0号节点到所有其他节点的距离,就是邻接数组的第一行。
需要一个数组来记录当前的节点相连的在生成树中的节点,记为 adjvex 数组,意为临近节点,初始化都为 0,
这两个数组通过下标来和节点一一对应。
然后我们需要比较生成树的边中,权值最小的边,也就是需要一个min变量,遍历所有的边,不断更新min,让它变成最小的权值,并且如果一个节点离生成树的距离最小,我们还要记录这个节点,记为 k。
k 节点需要加入到生成树中,也就是将 k 节点放在生成树中,也就是 k 节点对应的 lowcost[k] 要置为0,表示k节点到生成树的距离为0,权值和距离其实是一个意思。
将 k 节点加入到生成树中之后,还需要更新 adjvex 数组,想象一下,如果之前 0 -> 3 节点 没有路径,初始时 lowcost[3] = 正无穷,但是 0 -> 2 有路径,并且 2 -> 3 有路径,此时就需要更新 3 节点相连的节点,也就是 adjvex[3] 修改成 2,lowcost[3] = 2到3的距离,但是如果后面4号节点加入树,并且 4-> 3 的路径更短了,那就需要修改 adjvex[3] = 4lowcost[3] = 4到3的距离。所以说需要对比从 k 到 j 的距离和当前的 lowcost[j],如果k 到 j 的距离更小,需要更新lowcost[j] = k到j的距离adjvex[j] = k
上面流程走完之后,是实现了第一个权值最小的边的节点加入到生成树,所以整体需要循环n次,n为树的节点数
接下来根据主流程分析一下需要初始化的数据:
1、需要两层循环,i、j 作为循环变量
2、需要一个 min 值,初始化为一个c语言中的最大值
3、k 记录权值最小的边对应的节点
4、lowcost 数组,记录权值
5、adjvex 数组,记录邻近节点
需要定义的数据结构和常量:
1、图的结构体
2、MAXVEX 表示最大节点数
入参就是 图
接下来看实际的代码吧,相信通过上面的分析你已经非常清楚每句代码的作用

// 普利姆算法
#include <stdio.h>
#include <limits.h>#define MAXVEX 100  // 最大顶点数// 图的邻接矩阵表示
typedef struct {int vexnum;  // 顶点数int arcnum;  // 边数int arcs[MAXVEX][MAXVEX];  // 邻接矩阵
} MGraph;void MiniSpanTree_Prim(MGraph G){int min,i,j,k;int adjvex[MAXVEX]; // 保存邻接顶点下标的数组int lowcost[MAXVEX]; // 记录当前生成树到剩余顶点的最小权值lowcost[0] = 0; // 初始化第一个权值为0,即v0加入生成树adjvex[0] = 0; // 将0号顶点作为第一个顶点加入到树中for(i=1;i<G.vexnum;i++){ // 除了下标为0以外的所有顶点lowcost[i] = G.arcs[0][i]; // 将与下标为0的顶点有边的权值存入lowcost数组adjvex[i] = 0; // 这些顶点的adjvex数组元素都为0}// 算法核心for(i=1;i<G.vexnum;i++){  // 只需要循环vexnum-1次min = INT_MAX;  // 使用更合适的最大值j=0;k=0;// 找出lowcost中最小值,最小权值给min,最小值的下标给kfor(j=1;j<G.vexnum;j++){  // 从1号顶点开始找if(lowcost[j]!=0 && lowcost[j]<min){ // 不在生成树中的顶点而且权值更小的min = lowcost[j];  // 更新更小的值k = j;  // 找q到了新的点下标给k}}printf("边(%d,%d) 权值:%d\n",adjvex[k],k,min);  // 打印权值最小的边lowcost[k] = 0; // 将这个顶点加入生成树for(j=1;j<G.vexnum;j++){if(lowcost[j]!=0 && G.arcs[k][j]<lowcost[j]){ // 如果新加入的顶点k使权值更小lowcost[j] = G.arcs[k][j];  // 更新更小的权值adjvex[j] = k;  //修改这条边邻接的顶点}}}
}// 初始化测试图
void initTestGraph(MGraph *G) {int i, j;// 初始化邻接矩阵为无穷大(用大数值表示)for(i = 0; i < MAXVEX; i++) {for(j = 0; j < MAXVEX; j++) {if(i == j) {G->arcs[i][j] = 0;  // 自己到自己距离为0} else {G->arcs[i][j] = INT_MAX;  // 无边用最大值表示}}}// 设置顶点数G->vexnum = 6;  // 0-5共6个顶点// 添加边(无向图,需要设置两个方向)// 边 0-1,权值4G->arcs[0][1] = G->arcs[1][0] = 4;// 边 0-2,权值6  G->arcs[0][2] = G->arcs[2][0] = 6;// 边 0-3,权值16G->arcs[0][3] = G->arcs[3][0] = 16;// 边 1-2,权值10G->arcs[1][2] = G->arcs[2][1] = 10;// 边 1-4,权值7G->arcs[1][4] = G->arcs[4][1] = 7;// 边 2-3,权值14G->arcs[2][3] = G->arcs[3][2] = 14;// 边 2-4,权值3G->arcs[2][4] = G->arcs[4][2] = 3;// 边 2-5,权值8G->arcs[2][5] = G->arcs[5][2] = 8;// 边 4-5,权值9G->arcs[4][5] = G->arcs[5][4] = 9;
}int main() {MGraph G;printf("=== 普里姆最小生成树算法演示 ===\n\n");// 初始化测试图initTestGraph(&G);printf("图的顶点数:%d\n", G.vexnum);printf("图的边信息:\n");printf("0-1: 4, 0-2: 6, 0-3: 16\n");printf("1-2: 10, 1-4: 7\n");printf("2-3: 14, 2-4: 3, 2-5: 8\n");printf("4-5: 9\n\n");printf("开始执行普里姆算法:\n");printf("最小生成树的边:\n");// 执行普里姆算法MiniSpanTree_Prim(G);printf("\n================================\n");printf("算法执行完成!\n");printf("实际结果:总权值 = 4+6+3+8+14 = 35\n");return 0;
}

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

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

相关文章

构建强大的物联网架构所需了解的一切

数据正驱动着当今的商业发展&#xff0c;而物联网&#xff08;IoT&#xff09;则有助于为企业的增长和创新开辟新的机遇。麦肯锡的研究表明&#xff0c;全球数据在四年内实现了惊人的 7 倍增长。随着越来越多的物联网设备进入市场&#xff0c;更多企业开始需要强大的物联网架构…

java之json转excel生成

背景 业务为实现自定义样式excel的导出&#xff0c;常规的做法就是根据数据在代码中进行类似模版的配置&#xff1b;这样的体验不是很好&#xff0c;只要用户改变下样式的设置不用代码改动就能实现自定义excel的导出更加灵活。 以下是具体实现 pom依赖 <dependency><g…

新版本Cursor中配置自定义MCP服务器教程,附MCP工具开发实战源码

在 Cursor 中配置自定义 MCP 服务器&#xff1a;打造你的 AI 开发工具链 引言 随着 AI 编程助手的普及&#xff0c;开发者们越来越希望能够定制化自己的开发环境。Cursor 作为一款强大的 AI 编程编辑器&#xff0c;提供了 Model Context Protocol (MCP) 支持&#xff0c;新版本…

前端面试十二之vue3基础

一、ref和reactive在 Vue 3 中&#xff0c;ref 和 reactive 是两种主要的响应式数据创建方式&#xff0c;它们各有特点和适用场景。1.refref 主要用于创建单个值的响应式引用&#xff0c;通常用于基本类型数据&#xff0c;如数字、字符串等。使用 ref 创建的引用对象可以通过 .…

设计模式四:装饰模式(Decorator Pattern)

装饰模式是一种结构型设计模式&#xff0c;它允许你动态地给一个对象添加额外的职责&#xff0c;相比继承更加灵活。1. 模式定义装饰模式&#xff1a;动态地给一个对象添加一些额外的职责。就增加功能来说&#xff0c;装饰模式相比生成子类更为灵活。2. 模式结构主要角色&#…

神经网络常见激活函数 14-Mish函数

文章目录Mish函数导函数函数和导函数图像优缺点PyTorch 中的 Mish 函数TensorFlow 中的 Mish 函数Mish 论文 https://arxiv.org/pdf/1908.08681 函数导函数 Mish函数 Mish(x)x⋅tanh⁡⁣(softplus(x))x⋅tanh⁡⁣(ln⁡⁣(1ex))\begin{aligned} \text{Mish}(x) & x \cdot \t…

LAMP迁移LNMP Nginx多站点配置全流程

文章目录前言备份与停止服务nginx安装与配置nginx 编译安装配置服务php-fpm多站点配置phf-fpm介绍多站点配置nginx 多站点配置nginx ssl 配置参考前言 之前服务器使用的是 LAMP环境&#xff0c;想充分利用服务器资源&#xff0c;再运行另外一个站点 在LAMP环境下应该是也可以…

Nginx屏蔽国外IP访问

下载IP列表 # 下载到文件 wget http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest # 直接输出到终端 curl -sSL https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest得到一份国内IP配置 # 原始IP列表格式&#xff1a;apnic|CN|ipv4|218.78.0.0|1310…

stl-string模拟

1.介绍主要进行cpp中string的模拟&#xff0c;方便我们更好的对stl进行使用&#xff0c;string没有模板&#xff0c;我们将头文件和函数写在两个不同的文件2.头文件3.cpp文件如有问题&#xff0c;欢迎纠正&#xff01;

基于MATLAB的极限学习机ELM的数据回归预测方法应用

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取 或者私信获取。 1.项目背景 在当今的数据驱动时代&#xff0c;准确且高效的预测模型对于解决复杂问题至关重要。极限学习机&#…

芯谷科技--双四通道模拟/数字多路复用器74HC4052

在电子系统中&#xff0c;信号的多路复用与解复用是常见的需求&#xff0c;特别是在需要对多个信号源进行选择和切换的场景中。芯谷科技推出的 74HC4052 双四通道模拟/数字多路复用器/解复用器&#xff0c;以其高效、灵活的设计&#xff0c;为工程师提供了可靠的解决方案。产品…

基于MATLAB的极限学习机ELM的数据分类预测方法应用

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取 或者私信获取。 1.项目背景 在现代数据挖掘与机器学习领域&#xff0c;面对日益复杂的数据结构和快速增长的数据量&#xff0c;开…

复合机器人在生物制药实验室上下料搬运案例

在医疗行业的物料搬运环节&#xff0c;传统的人工操作模式逐渐暴露出诸多弊端&#xff0c;成为制约企业发展的瓶颈。富唯智能通过引入先进的复合机器人技术&#xff0c;为医疗企业提供了高效、智能的上下料搬运解决方案&#xff0c;助力医疗行业实现自动化与智能化升级。​客户…

嵌入式学习-PyTorch(7)-day23

损失函数的调用import torch from torch import nn from torch.nn import L1Lossinputs torch.tensor([1.0,2.0,3.0]) target torch.tensor([1.0,2.0,5.0])inputs torch.reshape(inputs, (1, 1, 1, 3)) target torch.reshape(target, (1, 1, 1, 3)) #损失函数 loss L1Loss…

用 Ray 跨节点调用 GPU 部署 DeepSeek 大模型,实现分布式高效推理

在大模型时代&#xff0c;单节点 GPU 资源往往难以满足大模型&#xff08;如 7B/13B 参数模型&#xff09;的部署需求。借助 Ray 分布式框架&#xff0c;我们可以轻松实现跨节点 GPU 资源调度&#xff0c;让大模型在多节点间高效运行。本文将以 DeepSeek-llm-7B-Chat 模型为例&…

快速了解 HTTPS

1. 引入 在 HTTP 协议 章节的 reference 段&#xff0c;曾提到过 HTTPS。这里对HTTPS进行详细介绍。 HTTPS 是在 HTTP 的基础上&#xff0c;引入了一个加密层 (SSL)。HTTP 是明文传输的 (不安全)。当下所见到的大部分网站都是 HTTPS 的。 起初是拜运营商劫持所赐&#xff08;…

mysql备份与视图

要求:1.将mydb9_stusys数据库下的student、sc 和course表&#xff0c;备份到本地主机保存为st_msg_bak.sql文件&#xff0c;然后将数据表恢复到自建的db_test数据库中&#xff1b;2.在db_test数据库创建一视图 stu_info,查询全体学生的姓名&#xff0c;性别&#xff0c;课程名&…

【数据结构】 链表 + 手动实现单链表和双链表的接口(图文并茂附完整源码)

文章目录 一、 链表的概念及结构 二、链表的分类 ​编辑 三、手动实现单链表 1、定义单链表的一个节点 2、打印单链表 3、创建新节点 4、单链表的尾插 5、单链表的头插 6、单链表的尾删 7、单链表的头删 8、单链表的查找 9、在指定位置之前插入一个新节点 10、在指…

Go语言时间控制:定时器技术详细指南

1. 定时器基础&#xff1a;从 time.Sleep 到 time.Timer 的进化为什么 time.Sleep 不够好&#xff1f;在 Go 编程中&#xff0c;很多人初学时会用 time.Sleep 来实现时间控制。比如&#xff0c;想让程序暂停 2 秒&#xff0c;代码可能是这样&#xff1a;package mainimport (&q…

C# 转换(显式转换和强制转换)

显式转换和强制转换 如果要把短类型转换为长类型&#xff0c;让长类型保存短类型的所有位很简单。然而&#xff0c;在其他情况下&#xff0c; 目标类型也许无法在不损失数据的情况下容纳源值。 例如&#xff0c;假设我们希望把ushort值转化为byte。 ushort可以保存任何0~65535的…