最小生成树,普利姆(Prim)算法及C/C++代码实现
内容摘要
1. 最小生成树(又名:最小权重生成树)概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。最小生成树属于一种树形结构(树形结构是一种特
文章正文
1. 最小生成树(又名:最小权重生成树)
概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。最小生成树属于一种树形结构(树形结构是一种特殊的图),或者说是直链型结构,因为当n个点相连,且路径和最短,那么将它们相连的路一定是n-1条。
可以利用参考一个问题理解最小生成树,有n个村庄,每个村庄之间距离不同,要求村庄之间修路,每一个村庄必须与任意一个村庄联通,如何修路最省钱(修的最短)
2. 普利姆算法介绍
利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图
具体过程如下:
(1)设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点集合,E,D是边的集合
(2)若从顶点u开始构造最小生成树,则从集合V中取出顶点u放入集合U中,标记顶点v的visited[u]=1
(3)若集合U中顶点ui与集合V-U中的顶点vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点vj加入集合U中,将边(ui,vj)加入集合D中,标记visited[vj]=1
(4)重复步骤②,直到U与V相等,即所有顶点都被标记为访问过,此时D中有n-1条边
3. 代码实现
不同的题目有不同的细节实现方式,因此本代码仅供参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | #include <stdio.h> #include <stdlib.h> #define n 20 #define MaxNum 10000 /*定义一个最大整数*/ /*定义邻接矩阵类型*/ typedef int adjmatrix[n + 1][n + 1]; typedef struct { int fromvex, tovex; //生成树的起点和终点 int weight; //边的权重 } Edge; typedef Edge *EdgeNode; //定义生成树的别名 int arcnum; /*边的个数*/ /*建立图的邻接矩阵*/ void CreatMatrix(adjmatrix GA) { int i, j, k, e; printf ( "=============================\n" ); printf ( "图中有%d个顶点\n" , n); for (i=1; i<=n; i++) { for (j=1; j<=n; j++) { if (i==j) { GA[i][j]=0; /*对角线的值置为0*/ } else { GA[i][j]=MaxNum; /*其他位置的值置初始化为一个最大整数*/ } } } printf ( "请输入边的个数:\n" ); scanf ( "%d" , &arcnum); printf ( "请输入边的信息,依照起点,终点,权值的形式输入:\n" ); for (k=1; k<=arcnum; k++) { scanf ( "%d,%d,%d" ,&i,&j,&e); /*读入边的信息*/ GA[i][j]=e; GA[j][i]=e; } } /*初始化图的边集数组*/ void InitEdge(EdgeNode GE, int m) { int i; for (i=1; i<=m; i++) { GE[i].weight=0; } } /*依据图的邻接矩阵生成图的边集数组*/ void GetEdgeSet(adjmatrix GA,EdgeNode GE) { int i, j, k = 1; for (i=1; i<=n; i++) { for (j=i+1; j<=n; j++) { if (GA[i][j] !=0 && GA[i][j] != MaxNum) { GE[k].fromvex = i; GE[k].tovex = j; GE[k].weight = GA[i][j]; k++; } } } } /*按升序排列图的边集数组*/ void SortEdge(EdgeNode GE, int m) { int i,j,k; Edge temp; for (i=1; i<m; i++) { k=i; for (j=i+1; j<=m; j++) { if (GE[k].weight > GE[j].weight) { k=j; } } if (k!=i) { temp = GE[i]; GE[i]=GE[k]; GE[k]=temp; } } } /*利用普里姆算法从初始点v出发求邻接矩阵表示的图的最小生成树*/ void Prim(adjmatrix GA,EdgeNode T) { int i,j,k,min,u,m,w; Edge temp; /*给T赋初值。相应为v1依次到其余各顶点的边*/ k=1; for (i=1; i<=n; i++) { if (i!=1) { T[k].fromvex=1; T[k].tovex=i; T[k].weight=GA[1][i]; k++; } } /*进行n-1次循环,每次求出最小生成树中的第k条边*/ for (k=1; k<n; k++) { min=MaxNum; m=k; for (j=k; j<n; j++) { if (T[j].weight<min) { min=T[j].weight; m=j; } } /*把最短边对调到k-1下标位置*/ 可用swap替换 temp=T[k]; T[k]=T[m]; T[m]=temp; /*把新增加最小生成树T中的顶点序号赋给j*/ j=T[k].tovex; /*改动有关边,使T中到T外的每个顶点保持一条到眼下为止最短的边*/ for (i=k+1; i<n; i++) { u=T[i].tovex; w=GA[j][u]; if (w<T[i].weight) { T[i].weight=w; T[i].fromvex=j; } } } } /*输出边集数组的每条边*/ void OutEdge(EdgeNode GE, int e) { int i; printf ( "依照起点,终点。权值的形式输出的最小生成树为:\n" ); for (i=1; i<=e; i++) { printf ( "%d,%d,%d\n" ,GE[i].fromvex,GE[i].tovex,GE[i].weight); } printf ( "=============================\n" ); } int main() { adjmatrix GA; Edge GE[n*(n-1)/2], T[n]; CreatMatrix(GA); InitEdge(GE,arcnum); GetEdgeSet(GA,GE); SortEdge(GE,arcnum); Prim(GA,T); printf ( "\n" ); OutEdge(T,n-1); return 0; } |
代码注释
[!--zhushi--]