一个连通图的生成树是一个极小的连通子图,它包含图中全部的顶点(n个顶点),但只有n-1条边。

最小生成树:构造连通网的最小代价(最小权值)生成树。

prim算法在严蔚敏树上有解释,但是都是数学语言,很深奥。

最小生成树MST性质:假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,

其中u∈U,v∈V-U,则必存在一颗包含边(u,v)的最小生成树。

prim算法过程为:

假设N=(V,{E})是连通图,TE是N上最小生成树中边的集合。算法从U={u0}(u0∈V),TE={}开始,

重复执行下述操作:

在所有u∈U,v∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入集合TE,同时v0 并入U,直至U=V为止。

此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。

我以图为例,看看算法过程。

上面基本就把prim算法思想给表达出来。

代码部分:

这里我使用的是邻接矩阵来表示图,其中边的值就是权值。

#define MAXVEX 100
#define IFY 65535 typedef char VertexType;
typedef int EdgeType;
//静态图-邻接矩阵
typedef struct {
VertexType vexs[MAXVEX];
EdgeType Mat[MAXVEX][MAXVEX];
int numVexs,numEdges;
}MGraph;
VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'};

EdgeType g_init_edges[MAXVEX][MAXVEX] = {
{,,IFY,IFY,IFY,,IFY,IFY,}, //'A'
{,,,IFY,IFY,IFY,IFY,IFY,}, //'B'
{IFY,,,,IFY,IFY,IFY,IFY,IFY},//'C'
{IFY,IFY,,,,IFY,IFY,IFY,IFY},//'D'
{IFY,IFY,IFY,,,,,,IFY}, //'E'
{,IFY,IFY,IFY,,,,IFY,IFY}, //'F'
{IFY,IFY,IFY,IFY,,,,IFY,IFY}, //'G'
{IFY,IFY,IFY,IFY,,IFY,IFY,,IFY}, //'H'
{,,IFY,IFY,IFY,IFY,IFY,IFY,}, //'I'
};

prim算法代码:

void prim(MGraph G,int num)
{
int sum=;
int min,i,j,k;
int adjvex[MAXVEX];
int lowcost[MAXVEX]; lowcost[num] = ;
adjvex[num] = ; for (i = ; i < G.numVexs;i++ )
{
if (num == i)
{
continue;
}
lowcost[i]=G.Mat[num][i]; //存放起始顶点到各个顶点的权值。
adjvex[i] = num;
} for (i=;i<G.numVexs;i++)
{
//1.找权最短路径
//2.把权最短路径的顶点纳入已找到的顶点集合中,重新查看新集合中最短路径
if(num == i)
{
continue;
}
min = IFY;
j=;k=;
while (j<G.numVexs)
{
if (lowcost[j] != && lowcost[j] < min)
{
min = lowcost[j];
k = j; }
j++;
}
printf(" (%d,%d) --> ",adjvex[k],k);
sum += G.Mat[adjvex[k]][k];
lowcost[k]=;
for (j=;j<G.numVexs;j++)
{
if (j == num)
{
continue;
}
if (lowcost[j] != && G.Mat[k][j] < lowcost[j])
{
lowcost[j] = G.Mat[k][j];
adjvex[j]=k;
}
}
}
printf("\ntotal:sum=%d",sum);
}

我写的是一个可以指定入口的(即从哪个点)开始进行。测试每个入口,得到的路径应该是一样,且值也应该一样大。

其中两个辅助数组:

lowcost[]:用来存放 非U集合的点与U集合点的权值的最小值。其【x】里面的数字x,表示U中到V中顶点Vx的最小权值。(每次都会更新比较,保证其最小。)

      而归入到U集合的点,对应的lowcost中的元素是为0;之后就不再做比较。

adjvex[]:在每次归入新顶点后,都要对U与非U集合中权值比较,保持lowcost中的值为最小。此时改变的lowcost中的某个元素(即新纳入的顶点到非U集合的权值更小)

      此时,将改变的lowcost中序号x,将新纳入的顶点Vt与原先U集合中与之相连的点的序号存入adjvex【x】。这样 adjvex【x】中,x就是那些个要更新的

      lowcost【x】,adjvex【x】存放就是原先点。

这也方便查找新加入的边(adjvex【k】,k)。

   

基本上可以看出,adjvex【】作用:

  实质上lowcost[x] 是 边(x,adjvex[x])的权值。明白这一点,程序就非常好理解了。

完整程序:

// grp-mat-bfs-self.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdlib.h> #define MAXVEX 100
#define IFY 65535 typedef char VertexType;
typedef int EdgeType; bool g_visited[MAXVEX]; VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'}; EdgeType g_init_edges[MAXVEX][MAXVEX] = {
{,,IFY,IFY,IFY,,IFY,IFY,}, //'A'
{,,,IFY,IFY,IFY,IFY,IFY,}, //'B'
{IFY,,,,IFY,IFY,IFY,IFY,IFY},//'C'
{IFY,IFY,,,,IFY,IFY,IFY,IFY},//'D'
{IFY,IFY,IFY,,,,,,IFY}, //'E'
{,IFY,IFY,IFY,,,,IFY,IFY}, //'F'
{IFY,IFY,IFY,IFY,,,,IFY,IFY}, //'G'
{IFY,IFY,IFY,IFY,,IFY,IFY,,IFY}, //'H'
{,,IFY,IFY,IFY,IFY,IFY,IFY,}, //'I'
}; EdgeType g_init_edges_bak[MAXVEX][MAXVEX] = {
{,,IFY,IFY,IFY,,IFY,IFY,}, //'A'
{,,,IFY,IFY,IFY,IFY,IFY,}, //'B'
{IFY,,,,IFY,IFY,IFY,IFY,IFY},//'C'
{IFY,IFY,,,,IFY,IFY,IFY,IFY},//'D'
{IFY,IFY,IFY,,,,,,IFY}, //'E'
{,IFY,IFY,IFY,,,,IFY,IFY}, //'F'
{IFY,IFY,IFY,IFY,,,,IFY,IFY}, //'G'
{IFY,IFY,IFY,IFY,,IFY,IFY,,IFY}, //'H'
{,,IFY,IFY,IFY,IFY,IFY,IFY,}, //'I'
};
//==========================================================================
//静态图-邻接矩阵
typedef struct {
VertexType vexs[MAXVEX];
EdgeType Mat[MAXVEX][MAXVEX];
int numVexs,numEdges;
}MGraph; //====================================================================
//打印矩阵
void prt_maxtix(EdgeType *p,int vexs)
{
int i,j;
for (i=;i<vexs;i++)
{
printf("\t");
for (j=;j<vexs;j++)
{
if( (*(p + MAXVEX*i + j)) == IFY)
{
printf(" $ ");
}
else
{
printf(" %2d ", *(p + MAXVEX*i + j));
}
}
printf("\n");
}
} //check the number of vextex
int getVexNum(VertexType *vexs)
{
VertexType *pos = vexs;
int cnt=;
while(*pos <= 'Z' && *pos >= 'A')
{
cnt++;
pos++;
}
return cnt;
} bool checkMat(EdgeType *p,VertexType numvex)
{
int i,j;
for (i=;i<numvex;i++)
{
for(j=i+;j<numvex;j++)
{
//printf("[%d][%d] = %d\t",i,j,*(p + MAXVEX*i + j));
//printf("[%d][%d] = %d\n",j,i,*(p + MAXVEX*j + i));
if (*(p + MAXVEX*i + j) != *(p + MAXVEX*j +i) )
{
printf("ERROR:Mat[%d][%d] or Mat[%d][%d] not equal!\n",i,j,j,i);
return false;
}
}
}
return true;
} void init_Grp(MGraph *g,VertexType *v,EdgeType *p)
{
int i,j;
// init vex num
(*g).numVexs = getVexNum(v); //init vexter
for (i=;i<(*g).numVexs;i++)
{
(*g).vexs[i]=*v;
v++;
} //init Mat
for (i=;i<(*g).numVexs;i++)
{
for (j=;j<(*g).numVexs;j++)
{
(*g).Mat[i][j] = *(p + MAXVEX*i + j);
}
}
if(checkMat(&((*g).Mat[][]),(*g).numVexs) == false)
{
printf("init error!\n");
exit();
}
} void prim(MGraph G,int num)
{
int sum=;
int min,i,j,k;
int adjvex[MAXVEX];
int lowcost[MAXVEX]; lowcost[num] = ;
adjvex[num] = ; for (i = ; i < G.numVexs;i++ )
{
if (num == i)
{
continue;
}
lowcost[i]=G.Mat[num][i]; //存放起始顶点到各个顶点的权值。
adjvex[i] = num;
} for (i=;i<G.numVexs;i++)
{
//1.找权最短路径
//2.把权最短路径的顶点纳入已找到的顶点集合中,重新查看新集合中最短路径
if(num == i)
{
continue;
}
min = IFY;
j=;k=;
while (j<G.numVexs)
{
if (lowcost[j] != && lowcost[j] < min)
{
min = lowcost[j];
k = j; }
j++;
}
printf(" (%d,%d) --> ",adjvex[k],k);
sum += G.Mat[adjvex[k]][k];
lowcost[k]=;
for (j=;j<G.numVexs;j++)
{ if (lowcost[j] != && G.Mat[k][j] < lowcost[j])
{
lowcost[j] = G.Mat[k][j];
adjvex[j]=k;
}
} }
printf("total:sum=%d\n",sum);
} int _tmain(int argc, _TCHAR* argv[])
{
MGraph grp;
//init
init_Grp(&grp,g_init_vexs,&g_init_edges[][]);
//print Matix
prt_maxtix(&grp.Mat[][],grp.numVexs); //prim(grp,4);
int i;
for (i=;i<grp.numVexs;i++)
{
prim(grp,i);
}
//prim(grp,3); getchar();
return ;
}

测试结果:

最小生成树一样,而且总权值也一样。

算法学习记录-图——最小生成树之prim算法的更多相关文章

  1. 算法学习记录-图——最小生成树之Kruskal算法

    之前的Prim算法是基于顶点查找的算法,而Kruskal则是从边入手. 通俗的讲:就是希望通过 边的权值大小 来寻找最小生成树.(所有的边称为边集合,最小生成树形成的过程中的顶点集合称为W) 选取边集 ...

  2. 算法学习记录-图——最短路径之Dijkstra算法

    在网图中,最短路径的概论: 两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点. 维基百科上面的解释: 这个算法是通过为每个顶点 v 保留目前为止所找到的从 ...

  3. 算法学习记录-图(DFS BFS)

    图: 目录: 1.概念 2.邻接矩阵(结构,深度/广度优先遍历) 3.邻接表(结构,深度/广度优先遍历) 图的基本概念: 数据元素:顶点 1.有穷非空(必须有顶点) 2.顶点之间为边(可空) 无向图: ...

  4. 算法学习记录-图——应用之关键路径(Critical Path)

    之前我们介绍过,在一个工程中我们关心两个问题: (1)工程是否顺利进行 (2)整个工程最短时间. 之前我们优先关心的是顶点(AOV),同样我们也可以优先关心边(同理有AOE).(Activity On ...

  5. 算法学习记录-图——最小路径之Floyd算法

    floyd算法: 解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包. 设为从到的只以集合中的节点为中间节点的最短路径的长度. 若最短路径经过 ...

  6. 算法学习记录-图——应用之拓扑排序(Topological Sort)

    这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...

  7. C++编程练习(10)----“图的最小生成树“(Prim算法、Kruskal算法)

    1.Prim 算法 以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树. 2.Kruskal 算法 直接寻找最小权值的边来构建最小生成树. 比较: Kruskal 算法主要是针对边来展开,边数 ...

  8. 最小生成树的Prim算法

       构造最小生成树的Prim算法    假设G=(V,E)为一连通网,其中V为网中所有顶点的集合,E为网中所有带权边的集合.设置两个新的集合U和T,其中集合U用于存放G的最小生成树的顶点,集合T用于 ...

  9. 数据结构与算法--最小生成树之Prim算法

    数据结构与算法--最小生成树之Prim算法 加权图是一种为每条边关联一个权值或称为成本的图模型.所谓生成树,是某图的一棵含有全部n个顶点的无环连通子图,它有n - 1条边.最小生成树(MST)是加权图 ...

随机推荐

  1. matplotlib 知识点13:绘制散点图(scatter函数精讲)

    散点图是指在回归分析中,数据点在直角坐标系平面上的分布图,散点图表示因变量随自变量而变化的大致趋势,据此可以选择合适的函数对数据点进行拟合. 用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间 ...

  2. mysql 维护添加远程主机访问

    https://www.cnblogs.com/JNUX/p/6936548.html

  3. JS=和==和===的区别

    1. = : 赋值运算,赋值使用2.== :比较运算,仅比较自动转换后的值是否相等,忽略 变量类型,如:'1' == 1 //true 3.=== : 比较运算,比较值和变量类型是否相等,如:'1' ...

  4. .net core API第一次试水

    这是我第一个API 项目他比普通的.NET CORE APPLICATION要多一个controllers层和appsetting.json 我们主要研究一下流程是怎么样的 在我尝试着改变launch ...

  5. P3375 【模板】KMP字符串匹配(全程注释,简单易懂)

    题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果你不知道这是什么意思也不要问,去百度搜[ ...

  6. win10 64位 mysql安装过程出现status显示failed

    mysql安装过程出现status显示failed,如下图: 由于我的电脑是64位系统,这里需要升级一个插件,即32位 visual C++ 2013 and  visual C++ redistri ...

  7. Open edX 配置 O365 SMTP

    配置LMS/Studio SMTP: 用到的文件如下:以下设置采用的root用户进行 /edx/app/edxapp/lms.env.json #|env文件 里包含一些功能开关 /edx/app/e ...

  8. Python 字符编码问题的处理

    python中的字符编码问题往往是初学者容易弄不明白的问题, 要想将这个问题搞清楚,需要先弄明白以下的概念 decode 和 encode 函数的作用 字符串字面量的编码格式 decode(str)  ...

  9. 分布式文件系统ceph介绍

    ceph哲学思想 1. 每个组件必须支持扩展 2.不存在单点故障 3.解决方案必须是基于软件的.开源的.适应能力强 4.任何可能的一切必须自我管理 存在的意义:帮助企业摆脱昂贵的专属硬件 ceph目标 ...

  10. struct结构体内存大小

    一. 基本原则 1. struct中成员变量的声明顺序,与成员变量对应的内存顺序是一致的: 2. struct本身的起始存储地址必须是成员变量中最长的数据类型的整倍数,注意是最长的数据类型,而不是最长 ...