简要整理最小生成树-Prim算法
大部分资料参考数据结构 陈越版
如果无向连通图是一个网图,那么它的所有生成树必有一颗边权值总和最小的生成树(可能不唯一),我们称其为最小生成树.
这次要整理的Prim算法是构造最小生成树的一种方法.
对于有n个顶点的连通图来说,最小生成树由图中的n个点和n-1条边构成,并且无回路.
Prim算法的大致思想:
1.找到任意一个点,以这个点为起点开始生成树.
2.不断加入与树相邻且权值最小的边和相应的顶点.
3.重复第2步直到全部顶点录入完毕,成为最小生成树.
第2步的关键是在未收录的顶点中找到与树相邻并离树最近的顶点.
加入后,在剩下的顶点中继续寻找权值最小的边.
图解1:
图解2:
这两副图都是最小生成树,总权值也一样都为19.
注意点:
找生成树的过程中可能最小两条权值一样的边,那么不同的选择可能导致两种情况.
第一种是这次不选,以后的步骤也会选到,即最终结果的树是一样的.
第二种则是以后的步骤选不到,但最终结果的树的权值是一样的.
思路上理解后,就可以实现具体代码.
实现代码需要两个辅助一维数组,parent和dist.
parent存放每个顶点的父节点,是树的一种表现形式.
dist是用来保存每个顶点距离树的最小权值.
具体代码一:
选自慕课的陈越老师的数据结构
(此为部分关键代码,部分声明没有给出,MGraph是邻接矩阵的类型,LGraph是邻接表的类型)
/* 邻接矩阵存储 - Prim最小生成树算法 */
Vertex FindMinDist( MGraph Graph, WeightType dist[] )//查找与树相连邻便且权值最小的点
{ /* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;//记录点
WeightType MinDist = INFINITY;//先设置成"无穷大"
for (V=0; V<Graph->Nv; V++) {//遍历所有的点
if ( dist[V]!=0 && dist[V]<MinDist) {
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else return ERROR; /* 若这样的顶点不存在,返回-1作为标记 */
}
int Prim( MGraph Graph, LGraph MST )//如果不需要保存构成后的最小树,MST部分可以省去
{ /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
//因为最小树一定是稀疏树,所以用邻接表储存
WeightType dist[MaxVertexNum], TotalWeight;//分别是 储存各顶点离树的权值,总权值
Vertex parent[MaxVertexNum], V, W;//分别是 每个顶点的父节点(记录树),上一个收录的顶点,这次将要收录的顶点
int VCount;//已经收录的顶点个数
Edge E;//要加到树中边,储存边的信息
/* 初始化。默认初始点下标是0 */
for (V=0; V<Graph->Nv; V++) {
/* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */
dist[V] = Graph->G[0][V];//以0为起始点
parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0 */
}
TotalWeight = 0; /* 初始化权重和 */
VCount = 0; /* 初始化收录的顶点数 */
/* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
MST = CreateGraph(Graph->Nv);
E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 */
/* 将初始点0收录进MST */
dist[0] = 0;
VCount ++;
parent[0] = -1; /* 当前树根是0 */
while (1) {
V = FindMinDist( Graph, dist );//寻找顶点
/* V = 未被收录顶点中dist最小者 */
if ( V==ERROR ) /* 若这样的V不存在 */
break; /* 算法结束 */
/* 将V及相应的边<parent[V], V>收录进MST */
E->V1 = parent[V];//v的父节点
E->V2 = V;
E->Weight = dist[V];
InsertEdge( MST, E );//把边插入到MST
TotalWeight += dist[V];//权值相加
dist[V] = 0;//把V归0
VCount++;
//更新dist
for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
if ( dist[W]!=0 && Graph->G[V][W]<INFINITY ) {
/* 若W是V的邻接点并且未被收录 */
if ( Graph->G[V][W] < dist[W] ) {//G[v][w]为两边权值,dist[w]为待更新的值
/* 若收录V使得dist[W]变小 */
dist[W] = Graph->G[V][W]; /* 更新dist[W] */
parent[W] = V; /* 更新树 */
}
}
} /* while结束*/
if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */
TotalWeight = ERROR;
return TotalWeight; /* 算法执行完毕,返回最小权重和或错误标记 */
}
还不会的可以去慕课上看视频,最小生成树.
具体代码二:
(自己手写,简化后,只返回最小权值)
#define max 110
#define inf 1000000
int map[max][max];//地图
int dist[max];
int n,m;//m是顶点数
int prim()
{
int t=0;//权合
int c=0;//收录顶点数
for(int i=1;i<=m;i++)//初始化dist
{
dist[i]=map[1][i];
}
dist[1]=0;//收录第一个
c++;
while(1)
{
int min=inf;//设置最小值
int v=-1;//即将收录的那个顶点
for(int i=1;i<=m;i++)//寻找最小值
{
if(dist[i]!=0&&dist[i]<min)
{
min=dist[i];
v=i;
}
}
if(v==-1)//已经没有可以收录的点,结束
break;
t+=dist[v];//权值相加
dist[v]=0;
c++;
for(int i=1;i<=m;i++)//更新dist
{
if(dist[i]!=0&&map[v][i]<dist[i])
{
dist[i]=map[v][i];
}
}
}
if(c<m)//资料不足
return 0;
else
return t;//返回权值合
}
学完配合实际运用效果更好,附上一道经典题,杭电oj1863畅通工程.
(对我的文章有什么好的建议欢迎评论或私聊我)