prim算法基础详解(无向赋权图的最小生成树MST)
带权图分为有向和无向,无向图的最短路径又叫做最小生成树,有prime算法和kruskal算法
生成树的概念:联通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树 生成树是联通图的极小连通子图。
所谓极小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成非连通图。生成树各边的权值总和称为生成树的权。
权最小的生成树称为最小生成树,常用的算法有prime算法和kruskal算法。这里仅介绍prime算法..
算法描述如下:
1.将所有的结点分成两个集合。一部分是在最小生成树中的点(记为集合A),另一部分是不在最小生成树中的点(即待加入的点,记为集合B)。
2.开始的时候取任意一个点v作为顶点加入最小生成树中(A),然后遍历集合B,选取一个点vj使得(v,vj)的权值最小,然后将vj加入A集合,B集合中删去vj(删去操作可以用一个intree[i]数组来进行操作)
3.重复2步骤直到所有点遍历完毕。
注意:这里说的重复步骤2是指每次找的与顶点v的边权值最小的结点!
结合例子来看一下。(如下图)
选择1为初始结点,intree[1]标记为1.
遍历剩下的6个结点,找到(1,vj)中weight最小的那一个,显然是3,让3加入集合A,即intree[3]标记为1
然后注意此时集合A中有了两个元素(结点1和结点3)开始的时候我认为是从3开始遍历找3与其他点的最小权值,但是再次注意我们每次只找与顶点(这里就1结点)边权值最小的点。
3结点入队后,1就可以与5结点间接相连了,所以每次在A中加入一个结点,都要更新顶结点与其他结点的关联与否以及权值。
这一点非常重要是算法的重要步骤。
正如此时(1,5) = 8 (1,4) = 5 (1,2) = 6 ,显然下一次应该加入A集合的是点4,所以:
接下来就是:
附上代码及测试样例。
#include <stdio.h> #include <string.h> #define maxn 10 #define INF 100000 int node; int G[maxn + 1][maxn + 1];/*构建的图,必须是一个无向连通赋权图,G[i][j]存取权值*/ int intree[maxn];/*结点i在最小生成树中*/ int minweight[maxn];/*到i点的最小权重*/ int sumweight; void initialize() { memset(intree,0,sizeof(intree)); memset(minweight,0,sizeof(minweight)); for(int i = 0 ; i <= maxn ; i++) for(int j = 0 ; j <= maxn ; j++) G[i][j] = INF; sumweight = 0; } void prime(int n) { int node,Minweight; for(int i = 1 ; i <= n ; i++) minweight[i] = G[1][i]; /*先把1结点放进最小生成树,那么要知道1到其余结点的最小权重*/ intree[1] = 1;/*1点进入集合A,最小生成树集*/ for(int i = 2 ; i <= n ; i++) /*一共n个点,已经把1加入最小生成树,那么还剩下n-1个点*/ { Minweight = INF; for(int j = 1 ; j <= n ; j++) { if(minweight[j] < Minweight && !intree[j]) { Minweight = minweight[j]; node = j; } } printf("选择的结点标号为:%d\n",node); intree[node] = 1; sumweight += Minweight; /*每次在A中加入新的点,更新顶结点到各节点的权重*/ for(int j = 1 ; j <= n ; j++) if(!intree[j] && minweight[j] > G[node][j]) /*更新B集合*/ minweight[j] = G[node][j]; } } int main() { int Node,Edge; int vertex1,vertex2,weight; initialize(); scanf("%d%d",&Node,&Edge); for(int i = 1 ; i <= Edge ; i++) { scanf("%d%d%d",&vertex1,&vertex2,&weight); G[vertex1][vertex2] = weight; G[vertex2][vertex1] = weight; } prime(Node); printf("%d\n",sumweight); return 0; } /* 7 9 1 2 6 1 3 2 1 4 5 2 5 2 2 7 4 3 5 8 4 6 7 4 7 5 5 6 10 答案是25 */