【算法】最小生成树Prime求解
1.概念
简单说,最小生成树是一副连通加权无向图中一棵权值最小的生成树。最小生成树其实是最小权重生成树的简称。
一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。
有关生成树和最小生成树的具体概念可以参考[维基百科]。
2.Prime方法求解代码示例
// Minimum spanning tree
// prime algorithm
#define N 6
#define MAXINT 0x7FFFFFFF
// 将顶点使用变好数字表示,如果是其他字符或者字符串表示可以通过Hash映射成数字,关于这部分在《数据结构与算法分析C语言描述》书中有讲到。
int v[N] = {0,1,2,3,4,5};
// 这里给出的是从命令行或者文件中读取到的邻接矩阵,基于两个假设:
// 1. 顶点到其自身设为0
// 2. 当前顶点到其他顶点如果没有连接则表示为0,不为0的地方表示两个顶点组成的边的权重
int adj_w[N][N] = {
{0, 6, 1, 5, 0, 0},
{6, 0, 5, 0, 3, 0},
{1, 5, 0, 5, 6, 4},
{5, 0, 5, 0, 0, 2},
{0, 3, 6, 0, 0, 6},
{0, 0, 4, 2, 6, 0}};
int prime(int v[], int adj_w[][N])
{
// 用来记录相应位置的顶点是否已经遍历过了
int vis[N] = {0};
// 用来记录从头开始的最小生成树生成过程,可以打印查看如何产生的
int path[N] = {0};
// 记录最小生成树的权重和
int sum = 0;
// 与当前顶点相关的边的权重记录
int dis[N] = {0};
// 与当前顶点相连的边中的最小权重值
int min_weight = 0;
// 在当前顶点处理过程中选择的下一个顶点,也就是生成的方向
int node = 0;
// 为了能够在后续的边权重中进行比较和记录,先把不相邻的顶点对应的邻接矩阵权重值设为无穷大
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) {
if (i!=j && adj_w[i][j]==0) {
adj_w[i][j] = MAXINT;
}
}
}
// 以第0个点为起点,先将与之相连的所有边的权重记录下来
path[0] = 0;
vis[0] = 1;
for (int i=0; i<n; i++)
{
dis[i] = adj_w[0][i];
}
for (int i=1; i<n; i++)
{
min_weight = MAXINT;
for (int j=i; j<n; j++) {
if (vis[j]==0 && dis[j]>0 && dis[j]<min_weight) {
min_weight = dis[j];
node = j;
}
}
if (min_weight==MAXINT) break;
vis[node] = 1;
path[i] = node;
sum += min_weight;
// 找到下一个节点后要更新与它相关的边的权重值
for (int j=0; j<n; j++) {
if (vis[j]==0 && adj_w[node][j]<dis[j])
dis[j] = adj_w[node][j];
}
}
printf("Traversal path: ");
for (int i=0; i<n; i++) {
printf("%d -> ");
if (n-1==i)
printf("\n");
}
return sum;
}
参考资料
[1] ACM第四站————最小生成树(普里姆算法) https://blog.nowcoder.net/n/d11f4dcf65dc4794ba09c134a42d35ab?from=nowcoder_improve
[2] 【ACM】最小生成树(Prim算法) https://blog.csdn.net/TwT520Ly/article/details/53543759