一. 最小生成树
1. 生成树
生成树:所有顶点均由边连接在一起,但不存在回路的图
特点:
- 生成树的顶点个数必须与图的顶点个数相同
- 生成树是图的极小连通子图,去掉一条边则非连通
- 一个有 n 个顶点的连通图的生成树有 n-1 条边
- 含有 n 个顶点, n-1 条边的图不一定是生成树
- 在生成树中在加一条边必然形成回路
- 任意两点间路径唯一
2. 最小生成树
最小生成树:给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那颗生成树称为该网的最小生成树,也叫最小代价生成树
构造最小生成树:
- 在生成树的构造过程中,图中 n 个顶点分属两个集合:
①已经落在生成树上的集合 U
②尚未落在生成树上的集合 V-U - 在所有连通 U 中顶点和 V-U 中顶点的边中选取权值最小的边
二. 普利姆(Prim)算法
1. 算法思想
- 设 N = (V,E) 是连通网, TE 是 N 上最小生成树中边的集合
- 初始令 U = {u0} (u0∈V),TE = { }, 如:V1
- 在所有 u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0)。如 :(V1,V3)
- 将(u0,v0)并入集合 TE,同时 v0 并入 U。
- 从 V1,V3 出发的权值最小的边为(V3,V6)
- 从 V1,V3,V6 出发的权值最小的边为(V6,V4)
依次类推:
2. 普利姆算法的实现
void MiniSpanTree_Prim(MGraph G)
{
/**
* 初始化
*/
int min, i, j, k;
int adjvex[MAXVEX]; //保存相关顶点的下标
int lowcost[MAXVEX]; //保存相关顶点间边的权值
//lowcost值为0,即此下标的顶点已加入生成树
lowcost[0] = 0; //V0加入生成树
adjvex[0] = 0; //初始化第一个顶点下标为0
//循环除下标为0外的全部顶点
for (i = 1; i < G.numv; i++)
{
lowcost[i] = G.arc[0][i]; //将和V0有关的边的权值(邻接矩阵第0行)存入数组
adjvex[i] = 0; //初始化都为V0的下标
}
/**
* 构造最小生成树
*/
for (i = 1; i < G.numv; i++)
{
min = INFINITY; //初始化最小权值
j = 1;
k = 0;
while (j < G.numv) //循环所有结点
{
//若该结点还未加入生成树且权值小于当前最小权值
if (lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j]; //让当前权值成为最小值
k = j; //用k存储当前下标
}
j++;
}
////while结束后找到了当前顶点到其邻接点的最小权值(邻接矩阵第0行的最小值)
printf("(%d,%d)",adjvex[k],k); //打印当前顶点到其权值最小的邻接点
lowcost[k] = 0; //当前顶点k已加入生成树,设置为0
//循环所有顶点
for (j = 1; j < G.numv; j++)
{
//若该结点还未加入生成树且该结点k的邻接点的权值小于此前这些点未被加入生成树的权值
//-----即是将现在k的未加入生成树的邻接点的权值与在k之前就加入了生成树的结点的现在未加入生成树的邻接点的权值比较
//-----相当于比较邻接矩阵第k行的值
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
{
lowcost[j] = G.arc[k][j]; //将较小权值存入lowcost
adjvex[j] = k; //将下标为k的顶点存入adjvex
}
}
}
}