对于现实生活中对于多地搭建最小代价的通信网络,n个城市之间需要搭建n-1条通信线路,其重在于n个城市之间最多的n✖(n-1)/2条线路的合理选择出n-1条。使用连通网表示,顶点表示城市,边权值表示通信代价。那么对于连通网可以建立不同的生成树,对于其权值之和最小的生成树,也是就是连通网的最小生成树,简称最小生成树。
本文前沿知识——图的存储结构_邻接矩阵表示。可查看本专栏内容—— 数据结构_图.
MST性质
构建最小生成树的算法,其中多数算法利用了最小生成树的简称MST的性质
MST:
假设N=(V,E)是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈ V-U,则必存在一棵包含(u,v)的最小生成树。
反证法证明:
- 文字叙述:假设网N的任何一棵最小生成树都不包含(u, v)。设T是连通网上的一棵最小生成树,当将边(u, v)加入到T中时,由生成树的定义,T中必存在一条包含(u, v)的回路。另一方面,由于T是生成树,则在T上必存在另一条边(u′, v′),其中u′∈U,v′∈V−U,且u和u′之间、v和v′之间均有路径相通。删去边(u′, v′),便可消除上述回路,同时得到另一棵生成树T′。因为(u, v)的权值不高于(u′, v′),则T′的权值亦不高于T,T′是包含(u, v)的一棵最小生成树。由此和假设矛盾。
- 图表述:
prim算法
算法思想
构造过程不断加点,别称“加点法”
- 构造步骤:
假设N=(V, E)是连通网,TE是N上最小生成树中边的集合。
(1) U={ u0 }(u0∈V),TE={ }。
(2) 在所有u∈U,v∈V−U的边(u, v)∈E中找一条权值最小的边(u0, v0)并入集合TE,同时v0并入U。
(3)重复(2),直至U=V为止。- 书上实例
代码实现
核心代码:
/// ===============================================
/// prim算法实现
/// ===============================================
struct E {
VertexType v;
Edgetype e;
}EDG[MAX];
//n个顶点,选择其中一个作为边起始,最多有n-1条边
//存储边信息,v存储的是(u,v)边,e存储边(u,v)的权值。
//从所有n∈U中选择最小边的操作就是,加入一个顶点进U时,先<u,i>(0<i<n)将数组的权值比它大的更新
//返回数组EDG中的最小边
int minE(E e[MAX],int n) {
int j = 0;
int min = -1;
for (int i = 0; i < n; ++i) {
if (j == 0) {
if (e[i].e != 0) {
min = i;
++j;
}
}
else {
if (e[i].e != 0) {
if (e[i].e < e[min].e)
min = i;
}
}
}
return min;
}
void prim(G_Amatreix g,VertexType v) {
int k = LocateV(g, v);
for (int i = 0; i < g.n; ++i) {
//用顶点存在的边<u0,i>将EDG初始化
if (i != k)
EDG[i] = {
v,g.E[k][i]};
else
EDG[i].e = 0; //e置为0,表示该加入U
}
for (int i = 1; i < g.n; ++i) {
//选择最小权值边并输出
k = minE(EDG,g.n);
printf("%c——%c\n", EDG[k].v, g.V[k]);
EDG[k].e = 0; //e置为0,表示该加入U
//用最小权值边的边终点存在的边<k,i>将EDG(若边权值比之前的小,则替换)
for (int j = 0; j < g.n; ++j) {
if (g.E[k][j] < EDG[j].e)
EDG[j] = {
g.V[k],g.E[k][j] };
}
}
}
测试结果:
kruskal算法
算法思想
构造过程不断加最小边,别称“加边法”
- 构造步骤:
假设N=(V, E)是连通网,将N中的边从小到大顺序排列。
(1) 初始化只有n个顶点的无