1.现在要选择这样一个生成树,也就是使总耗费最少。这个问题就是构造连通网的最小代价生成树(Minimum Cost Spanning Tree)简称为最小生成树的问题。一颗生成树的代价就是树上各边的代价之和。
构造最小生成树的算法有多种。大多算法利用了最小生成树的下列简称为MST的特性:
假设N=(V,{E})是一个连通网,(N代表连通网,V代表网的各个顶点,E代表网的边),U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必存在一颗包含边(u,v)的最小生成树。
2.反证法证明上面MST特性:
假设网N的任意一颗最小生成树都不包含(u,v).设T是连通网上的一颗最小生成树,当将边(u,v)加入T中时,由生成树的定义可知,T中必存在一条包含(u,v)的回路。
另一方面,由于T是生成树,则在T中必存在一条(u1,v1),其中u1属于U,v1属于V-U,且u,u1,v,v1必有路径相通。删去(u1,v1)便可消除上述回路,同时得到另一颗生成树T1。由于(u,v)的代价不高于(u1,v1),则T1的代价不高于T,T1是包含(u,v)的一颗最小生成树。由此可假设矛盾。
3.普里姆(prim)算法
假设N=(V,{E})是连通网,TE是N上最小的生成树中边的集合。算法从U={u0},(u0属于V),TE={}开始,重复执行下述操作:在所有u属于U,v属于V-U的边(u,v)属于E中,寻找一条代价最小的边(u0,v0)并入集合TE中,同时v0也并入集合U中,直到U=V为止。此时TE中必有n-1条边,而T=(V,{E})为N的最小生成树。
为实现这个算法,需要附设一个辅助数组closedge,以记录从U到U-V具有最小代价的边。对每个顶点vi属于V-U,在辅助数组中存在一个相应分量closedge[i-1],它包含两个域,其中lowcost存储该边上的权。vex存储附属在该边上的在U中的顶点。
/**
* 普里姆算法,最小生成树,
* 从图中第start个元素开始,生成最小树
*/
public void prim(int start) {
Integer vexNum = mVexs.length; //顶点的个数
int index = 0;//prim最小树的索引,即prims数组的索引
String[] prims = new String[vexNum]; //prim最小树的结果数组
int[] weights = new int[vexNum]; //顶点间的权值
prims[index++] = mVexs[start].data; //prim生成树的第一个顶点是从start开始的
/**
* 初始化顶点的权值数组;
* 将每个顶点的权值初始化为第start个顶点到该顶点的权值
*/
for (int i = 0; i < vexNum; i++) {
weights[i] = getWeight(start, i);
}
for (int i = 0; i < vexNum; i++) {
if (start == i) {
continue; //终止本次循环,进行下一次循环,break终止for中的循环,进行循环下面的语句
}
int j = 0, k = 0, min = INF;
//在未被加入最小生成树的顶点中,找出权值最小的顶点
while (j < vexNum) {
//当weights[j]=0时,意味着第j个顶点已经被排序过,或者说已经加入了最小生成树中
if (weights[j] != 0 & weights[j] < min) {
min = weights[j];
k = j; //将最小权值的顶点赋给k
}
j++;
}
/**
* 经过上面的while循环,则知在未被加入到最小生成树的顶点中,权值最小的是k
* 将第k个顶点加入到prims数组中
*/
prims[index++] = mVexs[k].data;
//并将第k个顶点的权值标记为0,意味着第k个顶点已经被排序过了或者说被加入到了最小生成树的结果中
weights[k] = 0;
//更新其他顶点到最小生成树中顶点的权值
for (j = 0; j < vexNum; j++) {
//获取第k个顶点到第j个顶点的权值
int tmp = getWeight(k, j);
//当第j个顶点未在最小生成树中,且第j个顶点到最小生成树中顶点的权值需要更新时,即加入k顶点后,已不再是最小权值时
if (weights[j] != 0 && tmp < weights[j]) {
weights[j] = tmp;
}
}
}
/**
* 当prim最小生成树已经生成完毕时
* 计算最小生成树的权值
*/
int sum = 0;
//要从第2个元素开始,索引为1,因为要计算两顶点之间的权值
for (int i = 1; i < index; i++) {
int min = INF;
//或者去prims[i]顶点在顶点中的位置
int n = getPostion(prims[i]);
for (int j = 0; j < i; j++) {
int m = getPostion(prims[j]);
int tmp = getWeight(m, n);
if (tmp < min) {
min = tmp;
}
}
sum += min;
}
/**
* 打印最小生成树
*/
System.out.println("PRIM" + mVexs[start].data + "=" + sum);
for (int i = 0; i < index; i++) {
System.out.println(prims[i]);
}
}