如果你对数据结构了解不深,又想要学习普里姆算法,建议你看看我这篇,我尽量往细致的方向写,如果有些大佬认为我写的比较啰嗦,请见谅,毕竟我的目的是为了让数据结构小白能够在理解普里姆算法上相对容易一些。由于为了清楚地说明普里姆算法,可能会牺牲一些严谨性,但只讲严谨的术语会加大理解难度,尤其对于非科班出身的人。如遇大佬,请勿喷。

发文的目的

    我个人学习图结构已经有相当长一段时间了,图这块对于初学者来说真的相当难学,建议在学习数据结构之前先修计算机数学或者离散数学。我学习数据机构是为了毕业设计服务,本身并非计算机出身,学起来有难度。主要是为路径规划服务。非科班出身可以参考一下我的学习经验。

全文结构

    由于要详解,全文的内容可能会比较长,先做个提纲,可以从自己需要的部分看下去,不过建议从头看。
  1. 图结构的必要了解知识
  2. 普里姆算法简介
  3. 算法详解
  4. 总结

1、树的必要了解知识

    图结构是一种非线性数据结构,每个节点都可能有不止一个前驱和后继,这是它不同于树的地方。树的术语极多,要注意理解。关于树的存储,至少要理解邻接矩阵法。推荐的视频有小甲鱼的《数据结构》和入门书籍《大话数据结构》。建议初学者结合起来看。这里简单说一下要用到的概念。树的邻接矩阵存储方式,采用一个一维数组存储顶点,一个二维数组存储各边上的权值,两个顶点非连通则使用∞表示,在程序中使用一个所有边的权值都不可能达到的一个较大的数,例如65535,。自身到自身的顶点的边的权值用0表示。其他必要信息,例如顶点数,边数,可以用整型数表示。讲到这里,我们很自然地想到用一个结构体变量来定义和存储数结构。

2、普里姆算法简介

    普里姆算法是用来解决求图的最小生成树的经典算法。即求连通图的n个顶点的n-1条边的权值之和最小。使用这个算法有个称之为MST的性质,首先假设实际网N(带权图)是一个连通网(从图的任意一个顶点出发,都能到达其他的顶点),对于网N,已经找到的最小生成树的顶点必然是最终的最小生成树的顶点,说简单一点,就是你已经找到顶点V3是当前的最小生成树的顶点,这个点你可以不用找了,放心大胆地找其余的点,不会有更优的路径。这一点详见严蔚敏老师《数据结构》。如果你对图结构有一定了解但不熟悉,从这里开始看。关于普里姆算法的流程。它的大致步骤如下:1、从任意一个顶点开始(一般是第一个顶点),搜索与它相关联的顶点的最小权值的边,把这个顶点纳入保存已经找到的最小生成树的顶点的集合中,边并入最小生成树边的集合,这个顶点以后不再继续搜索;2、从找到的这个顶点开始,用之前的顶点的边权值修正这个顶点的边权值,取两者之中最小的。3、从修正后的顶点中继续找最小权值的边,保存顶点和边到最小生成树的点集和边集中。4、重复步骤2,3,直到最小生成树的顶点集达到图的顶点数停止。普里姆算法本质上是图的遍历和求最小值。但它既不是深度优先算法,也不是广度优先算法,结合实例会理解的更深刻一些。

3、算法详解

    有了前面的铺垫,再继续讲会更容易理解一些,如果你已经学习过图结构,但是对于普里姆算法又不是很理解,很会用。建议从这里开始看起。结合实例来说明普里姆算法。实例来源于《大话数据结构》,如果想详细了解,可以阅读原文。问题描述如下:如下图,求最小生成树


邻接矩阵

对下面的代码做入下解释
∞–INF;
由于本例中的图是无向图,它的邻接矩阵是对称阵,所以可以输入其中一半,另一半直接赋值。对于稀疏图,可以采用把邻接矩阵的所有元素都赋值为INF,然后把相互连通的顶点的边权值赋值。lowcost数组存放当前正在搜索的边权值,adjvex数组存放已经找到的最小生成树顶点。

代码的主要流程

  1. 将lowcost数组初始化为图的与第一个顶点V0相关联的边的权值,将第一个顶点V0纳入最小生成树的顶点,这个点对应的列权值以后不再搜索;
  2. 搜索lowcost数组中的最小值(v1),记下最小值的标号给k(1),这个点将作为找到变的终点(后继)以后的lowcost的第0和1个元素不在搜索(置0),做记号,而搜索其余的元素2,3,…,8;
  3. 更新lowcost,把和新找到的这个顶点(v1)相关联的边的权值(0,1不再参与搜索)和lowcost数组的对应元素作比较,找出更小的权值,保存在lowcost中,从图中来看就是,从已经找到的顶点(V0,V1)向下搜索,找最小值,如果一个顶点和两个已经找出的顶点都相连通,那么取两条边权值较小的那一个。并把k对应的值作为找到的边的起点(前驱)。
  4. 重复2,3过程,直到所有的顶点都被遍历,即所有的lowcost都被置0;
    在这里对比较难细节性的问题再做一下说明。主要是第二个for循环那里,他做了两件事,一是用找到的这一个新的顶点(k)的这一顶点的所有边去更新lowcost。如果你愿意的话,你可以用普里姆算法的流程在人脑里执行这个过程。举个例子,在我们已经找到v0,v1之后的下一步,你要查找和v0,v1相关联的边{(v0,v5),(v1,v2),(v1,v6),(v1,v8)},这个更新的过程对人来说的流程是先从v0找起,找到和它相关联的边,再找v1。但对于计算机来说就不是这样,它不管你是v0还是v1,把v0和v1的值都比较一番,谁小就取谁,显然(v0,v5)小,不仅如此,每次把比较之后修改的值也做上记号,好知道他是来自哪里的数据,比如,经过比较,发现(v1,v2)小,把它的顶点信息记在adjvex[2](1)里面,作为前驱。人和计算机的逻辑差别有点像两种招人方式,有的单位招人看学校和成绩,有的公司看能力,不管你的学校和学历,只要你干得好,初中我都要。
    总结一下,普里姆算法就干了三件事。第一,更新lowcost,始终都把当前能找到的最小路径放在里面,而且记下被修改的数据的顶点信息,以方便后面找它的根(前驱);第二,在更新后的lowcost中搜索最小值,保存编号到k中,这是下一个要更新的顶点序列的编号(后继);第三,有前驱有后继,把这条路径打印出来。
    代码入下
#include <stdio.h>
#define INF 65535
#define MAX 10/*定义图结构体*/
typedef struct
{char Vertex[MAX];      //顶点数组int arc[MAX][MAX];        //邻接表int numVertex, numEdges;   //顶点数,边数
}MGraph;/*图的初始化,顶点数,邻接矩阵*/
void GraphIint(MGraph *G)
{int i, j;printf("input numVertex less than %d:", MAX);       //初始化顶点数 scanf("%d", &G->numVertex);/*初始化邻接矩阵*/for(i = 0; i < G->numVertex; i++){for(j = i; j < G->numVertex; j++){printf("arc[%d][%d]", i, j);scanf("%d", &G->arc[i][j]);G->arc[j][i] = G->arc[i][j];     //对称阵 }printf("\n");}/*调试输出用*/for(i = 0; i < G->numVertex; i++){for(j = 0; j < G->numVertex; j++){printf("%d\t", G->arc[i][j]);}printf("\n");}/*调试输出用*/}
/*普里姆算法最小生成树*/
void MiniSpanTree_Prim(MGraph G)
{int min, i, j, k;      //min保存当前最短路径的边的权值,k保存当前最短路径的顶点编号 int lowcost[MAX];      //保存相关边的权值 int adjvex[MAX];     //保存相关顶点的权值 /*初始化lowcost和adjvex*/lowcost[0] = 0;           //初始化lowcost的第一个值   adjvex[0] = 0;         //初始化adjvex的第一个值/*初始化lowcost,adjvex的剩下的值*/   for(i = 1; i < G.numVertex; i++){lowcost[i] = G.arc[0][i];       //初始化lowcost为V0顶点的相关边权值(v0的行权值) adjvex[i] = 0;                    }/*Prim真正核心*/for(i = 1; i < G.numVertex; i++){min = INF;                    //初始化min,保证每次都从连通的路径开始搜索 k = 0;                     //从第一个顶点开始搜索 j = 1;                        //第一个顶点已经被认为是最小生成树的节点,无需从0开始 /*搜索当前lowcost数组的最小值,保存这个最小值的编号*/while(j < G.numVertex){if((lowcost[j] != 0) && (lowcost[j] < min))        //lowcost为0,表示该列已经搜索,逐行搜索时不再 {                                                //搜索lowcost中位于这一列的元素  min = lowcost[j];k = j;             //保存最小值的编号  }               j++;}/*打印路径边 */printf("(%d,%d)", adjvex[k], k);/*将已经找到的最小生成树的边置0,下次搜索时不再继续搜索该边 */    lowcost[k] = 0;                        /*更新lowcost数组*/for(j =1; j < G.numVertex; j++){/*比较lowcost和被筛选出的顶点的边(行权值),取较小值,保存在lowcost中*/if((lowcost[j] != 0) && (G.arc[k][j] < lowcost[j])){lowcost[j] = G.arc[k][j];adjvex[j] = k;             //标记k为最小生成树的顶点 }}}
} int main(void)
{MGraph G = {0};               //创建图 GraphIint(&G);                //图的初始化 MiniSpanTree_Prim(G);       //普里姆最小生成树 return 0;
}

程序运行结果

4、总结

    为什么我要花这么多的笔墨说明普里姆算法呢,第一它确有实际的工程应用,第二,后面要介绍的迪杰斯特拉算法(最短路径算法)和它有密切联系。普里姆算法算是数据结构中比较让人费解的算法之一了花费了我很长时间理解,这里把我遇到的问题和我的思考和大家分享一下,我个人不是很提倡老师上课的那一套,就是默认我讲的你都会,对于某些细节问题不展开讲,靠自己理解,这当然无可厚非,但是对于工程实践来说,没有细节是不行的,最多就是搬运工,所以我尽可能细致地往下讲,里面可能会有一些表述上的问题,但是只要你理解了,我想这些描述上的问题对你来说就不是问题。最后谈一下我在自学数据结构方面的经验。第一,如果有时间有精力的话,建议一定先学完计算机数学或者离散数学,再开始学习数据结构;第二,如果你不能忍受枯燥的讲解,开始最好找一些讲的比较生动的课程学习,最好是视频+书籍+练习,开始推荐程杰的《大话数据结构》作为入门级的书籍。如果你觉得没问题了,可以回头看看严蔚敏的《数据结构》,写的相当严谨,用数学语言描述的,简洁,优美。写这篇文章真的很不容易,中间电脑还断网了,有一段没有保存上,重写的,觉得好的话,厚着脸求个赞。

参考文献

  1. 程杰. 大话数据结构.[M]. 北京:清华大学出版社,2011.
  2. 严蔚敏,吴伟民.数据结构:C语言版[M]. 北京:清华大学出版 社,2007.

深度学习普里姆算法(Prim)相关推荐

  1. 普里姆算法(Prim)和克鲁斯卡尔(Kruskal)算法

    普里姆算法(Prim)和克鲁斯卡尔(Kruskal)算法 普里姆算法的基本思想: 取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w.添加顶点w的条件为:w 和已在生成树上的顶点v ...

  2. (数据结构)图的最小生成树 普里姆算法(Prim)

    假设要在n个城市之间建立通信联络网,每两个城市之间建立线路都需要花费不同大小的经费,则连通n个城市只需要n-1个条线路,最小生成树解决的问题就是:如何在最节省经费的前提下建立这个通信网 也可以理解为: ...

  3. Java用普里姆算法(prim)解决修路最短路径问题

    14.6 普里姆算法 14.6.1 应用场景-修路问题 看一个应用场景和问题: 有胜利乡有 7 个村庄(A, B, C, D, E, F, G) ,现在需要修路把 7 个村庄连通 各个村庄的距离用边线 ...

  4. 【算法】普里姆算法 Prim算法解决修路问题

    文章目录 1. 概述 1.1 最小生成树 2.普里姆算法介绍 3.代码 3.1 案例1 1. 概述 视频:https://www.bilibili.com/video/BV1E4411H73v?p=1 ...

  5. (迪杰斯特拉)Dijkstra算法 与 普里姆算法(Prim算法)

    怎么硕呢 这俩肯定是一个人抄了另一个人的代码.就在花费那一部分  一个是d[u] = mp[u][v]+d[v] (迪杰斯特拉)  另一个是d[u] = mp[u][v] 大体思路就是一直找和以之节点 ...

  6. 最小生成树普里姆算法Prim

    目录 前言 一.Prim算法的基本思路 二.代码实现 总结 前言   无论是什么程序都要和数据打交道,一个好的程序员会选择更优的数据结构来更好的解决问题,因此数据结构的重要性不言而喻.数据结构的学习本 ...

  7. 求最小生成树-Prim(普里姆算法)

    普里姆算法时间复杂度为O(V^2),适用于稠密图 #include <iostream> using namespace std; #define Maxsize 100 typedef ...

  8. 最小生成树(普里姆算法【Prim】与克鲁斯卡尔算法【Kruskal】)

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  9. JavaScript实现prim普里姆算法(附完整源码)

    JavaScript实现prim普里姆算法(附完整源码) Comparator.js完整源代码 Heap.js完整源代码 MinHeap.js完整源代码 PriorityQueue.js完整源代码 G ...

最新文章

  1. 往往存储与计算机硬盘或其他,硬盘是计算机系统中信息资源最重要的存储设备其所存放信息-Read.DOC...
  2. 了解下WSDL 文档
  3. java几种多线程_Java多线程实现的几种方式
  4. 大虾对51单片机入门的经验总结
  5. Spoken English-口语-练习频次
  6. ESXI 6.7安装并部署主机
  7. 老也有错?科技行业对大龄程序员的歧视
  8. 普通html优化处理,iOS 数据优化之处理HTML字符串
  9. IPv4子网划分基础
  10. OC_UISlider
  11. 说ViewHolder
  12. pyserial库是python语言用于,python的pyserial模块
  13. A* operator/(A*, A*)‘ must have an argument of class or enumerated type
  14. 量子领域又有新突破:量子态持续时间可超5秒
  15. pstack 安装linux_linux下跟踪进程调用栈strace pstack gstack
  16. 计算机控制门禁,实验室智能门禁管理系统
  17. 运动规划,路径规划和轨迹规划的关系(补充路径跟踪和轨迹跟踪)
  18. VN-SGG JavaScript 基础(中)
  19. ubuntu16.04搜狗输入法设置
  20. unity2D横板游戏教程6-敌人AI以及受击动画

热门文章

  1. cmd重启网络服务命令
  2. 计算机技术三大支柱,信息技术三大支柱常见七大传感器全解
  3. 简单实用的js模板引擎
  4. 度娘计算机cpu,CPU硬件虚拟化技术和360“核晶防护引擎”(进一步强化电脑性能)...
  5. pycharm在C盘创建.pycharm2019.3严重占用内存的解决方法
  6. python导入模块方法_Python导入模块的3种方法(非常详细)
  7. Python导入模块但显示模块中的函数不存在
  8. 收官之战 Power AI编程马拉松第四场圆满结束
  9. 我与酷派手机N900+的艰难之旅
  10. PHPExcel导入Excel时长数字变成科学计数法调整