今天就跟大家聊聊有关Prim算法原理是什么以及完整C代码的实现,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
生成树: 一个连通图的生成树是它的极小连通子图,在n个顶点的情形下,有n-1条边。生成树是对连通图而言的,是连通图的极小连通子图,包含途中所有顶点,有且仅有n-1条边。非连通图的生成树则组成一个声称森林;若图中有n个顶点,m个连通分量,则生成森林中有n-m条边。
图的遍历: 和树的遍历相似,若从图中某顶点出发,访问遍途中每个顶点,且每个顶点仅访问一次,此过程称为图的遍历。图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。图的常用遍历顺序有两种:深度优先搜索(DFS)和广度优先搜索(BFS),对每种搜索顺序,访问各顶点的顺序也不是唯一的。
在一个无向连通图G中,其所有顶点和遍历该图经过的所有边所构成的子图G',称作图G的生成树。一个图可以有多个生成树,从不同的顶点除法,采用不同的遍历顺序,遍历时所经过的边也就不同。
最小生成树:在图论中,常常将树定义为一个无回路连通图。对于一个带权的无向连通图,其每个生成树所有边上的权值之和可能不同,我们把所有边上权值之和最小的生成树成为图的最小生成树(MST)。
MST性质:MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
基本思想:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:
在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。
Prim算法的核心:始终保持TE中的边集构成一棵生成树。
Prim算法举例:
采用的是顶点数为6的无向连通图:
设集合V={A,B,C,D,E,F},即所有顶点的集合。
集合U为最小生成树的结点。
按照Prim算法:
其生成过程图示如下:
将A加入U,此时,U={ A },V-U={ B,C,D,E};
与A邻接的顶点有B,C,D。(A,B)、(A,C)、(A,D),权值分别为6、1、5,因而选定(A,C)为最小生成树的一条边;
将上一步选定的在V-U中的顶点C加入U,此时,U={A,C}, V-U={ B,D,E,F};
V-U中与U中顶点组成的边有(A,B)、(A,D)、(C,B)、(C,D)、(C,E)、(C,F),权值分别为6、5、5、5、6、4,因而选定(C,F)为最小生成树的一条边;
将F加入U中,此时U={ A,C,F} , V-U={ B, D,E};
V-U中与U中顶点组成的边有(A,B)、(A,D)、(C,B)、(C,D)、(C,E)、(F,D)、(F,E),权值分别为6、5、5、5、6、2、6,选定(F,D)为最小生成树的一条边;
将D加入U中,此时,U={ A,C,F,D}, V-U={ B,E};
V-U中与U中顶点组成的边有(A,B)、(C,B)、(C,E)、(F,E),权值分别为6、5、6、6,选定(C,B)为最小生成树的一条边。
将B加入U中,此时U= {A,C,F ,D,B }, V -U={E };
V-U中与U中顶点组成的边有(B,E)、(C,E)、(F,E),权值分别为3、6、6,选定(B,E)为最小生成树的一条边。
将E加入U中,此时U={A ,C,F,D,B,E},完成MST的生成。
难点是prim函数中的两个辅助数组的具体含义:lowcost数组,顾名思义,最小代价。也就是 lowcost[k] 保存着V-U中编号为k的顶点到U中所有顶点的最小权值。closest数组,顾名思义,距离最近。 也就是 closest[k] 保存着U中到V-U中编号为K的顶点权值最小的顶点的编号。这两个数组的元素是随着顶点不断加入U集合而动态变化的。程序中采用邻接矩阵法创建图。
/* 求最小生成树的prim算法 */ #include <stdio.h> #include <string.h> #define MaxSize 20 #define MAX 10000 typedef char VertexType; //定义图 的邻接矩阵表示法结构 typedef struct Graph { VertexType ver[MaxSize+1]; int edg[MaxSize][MaxSize]; }Graph; //邻接矩阵法图的生成函数 void CreateGraph( Graph *g ) { int i = 0; int j = 0; int VertexNum; VertexType Ver; printf("请输入图的顶点:\n"); while( '\n' != (Ver=getchar()) ) g->ver[i++] = Ver; g->ver[i] = '\0'; VertexNum = strlen(g->ver); printf("请输入相应的的邻接矩阵:\n"); for( i=0; i<VertexNum; i++ ) for( j=0; j<VertexNum; j++ ) scanf("%d", &g->edg[i][j]); } //打印图的结点标识符和邻接矩阵 void PrintGraph( Graph g ) { int i, j; int VertexNum = strlen(g.ver); printf("图的顶点为:\n"); for( i=0; i<VertexNum; i++ ) printf("%c ", g.ver[i]); printf("\n"); printf("图的邻接矩阵为:\n"); for( i=0; i<VertexNum; i++ ) { for( j=0; j<VertexNum; j++ ) printf("%d ", g.edg[i][j]); printf("\n"); } } //求图的顶点数 int CalVerNum( Graph g ) { return strlen(g.ver); } //将不邻接的顶点之间的权值设置为MAX void SetWeight( Graph *g ) { for( int i=0; i<CalVerNum(*g); i++ ) for( int j=0; j<CalVerNum(*g); j++ ) if( 0 == g->edg[i][j] ) g->edg[i][j] = MAX; } //运用prim算法求最小生成树 void prim( Graph g, int VerNum, int *parent ) { int i, j, k; int lowcost[MaxSize]; int closest[MaxSize], used[MaxSize]; int min; for( i=0; i<VerNum; i++ ) { //对辅助数组lowcost和closest进行初始化 lowcost[i] = g.edg[0][i]; closest[i] = 0; used[i] = 0; //used[i] == 0 表示i顶点在U中,反之,在V-U中。 parent[i] = -1; } used[0] = 1; //第一步将编号为0的顶点放入U中,也可以是其他顶点 for( i=0; i<VerNum-1; i++ ) { j = 0; min = MAX; for( k=1; k<VerNum; k++ ) //找到V-U中的与U中顶点组成的最小权值的边的顶点编号 if( (0==used[k]) && (lowcost[k]<min) ) { min = lowcost[k]; j = k; } parent[j] = closest[j]; used[j] = 1; //将j顶点加入U中 for( k=0; k<VerNum; k++ ) //由于j顶点加入U中,更新lowcost和closest数组中的元素,检测V-U中的顶点到j顶点的权值是否比j加入U之前的lowcost数组的元素小 if( (0==used[k]) && (g.edg[k][j]<lowcost[k]) ) { lowcost[k] = g.edg[k][j]; closest[k] = j; //closest数组保存的是U中到V-U中最小权值的顶点编号 } } } //打印最小生成树的边和MST的权值 void PrintMST( Graph g, int *parent ) { int VerNum = CalVerNum( g ); int weight = 0; printf("MST的边为:\n"); for( int i=1; i<VerNum; i++ ) { //VerNum-1条边 printf("%c--%c\n", g.ver[parent[i]], g.ver[i] ); weight+=g.edg[parent[i]][i]; } printf("MST的权值为:%d\n", weight); } int main() { Graph g; int parent[20]; CreateGraph ( &g ); PrintGraph( g ); SetWeight( &g ); prim( g, CalVerNum(g), parent ); PrintMST( g, parent ); return 0; }
测试结果:数据即为前面所讲的图。
看完上述内容,你们对Prim算法原理是什么以及完整C代码的实现有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。