普利姆算法
把构造连通网的最小代价生成树称为最小生成树。
我们通过构造一个图来说明这个算法。
对这个图写出其arc邻接矩阵:
在之前的图的存储就够中,已经有了一个存储结构为MGragh的邻接矩阵,其中,INFINITY代表∞,我们用数字65535来表示。现在,我们来看其核心代码:
public void MinSpanTree(){
int min,j,k;
int[] adjvex = new int[this.vertexs];//记录顶点下标的数组
int[] lowcost = new int[this.vertexs];//记录顶点间边的权值
adjver[0] = 0;//初始化,将第一个置为0
lowcost[0] = 0;//第一个值为0,说明第一个顶点A0已经加入了生成树
for(int i=1;i<this.vertexs;i++){
lowcost[i] = this.arc[0][i];//将与A0相连的顶点间的权值全部加入到此数组
//即,将arc矩阵的第一行加入到此数组
adjvex[i] = 0;//全部置为零
}
//到此,算法的准备工作全部做完
System.out.println("图的最小生成树为:");
for(int i=1;i<this.vertexs;i++){//第一个for循环
min = INFINITY;//初始化,将65535赋予min
j = 1;
k = 0;
while(j<this.vertexs){//循环全部的顶点
//判断此顶点是否访问过并且该顶点是否小于min值
if(lowcost[j]!=0&&lowcost[j]<min){
min = lowcost[j];//将更小的值赋予min
k = j;//用k来保存该顶点的下标
}
j++:
}
//打印当前顶点边中权值最小边,如打印 (0,1) 权值:1
System.out.println("("+adjvex[k]+","+"k"+")"+" 权值:"+lowcost[k]);
lowcost[k] = 0;//将访打印过的顶点置为0,表示已被访问过
for(j=1;j<this.vertexs;j++){//第二个for循环
//判断顶点是否访问过并且arc矩阵中k行的所有顶点间权值是否小于lowcost数组中原来的值
if(lowcost[j]!=0&&this.arc[k][j]<lowcost[j]){
lowcost[j] = this.arc[k][j];//若小于,将该值替换
adjvex[j] = k;//将访问过的顶点下标k放进adjvex数组
}
}
}
}
通过上述代码,我们将开始的图带入该算法,一步一步进行计算。
开始时:lowcost=[0,1,1,4];adjvex=[0,0,0,0];
1. while循环后: min=1,k=1 打印(0,1),然后lowcost[1] = 0;
2. 第二个for循环后:lowcost=[0,0,1,4],adjvex=[0,0,0,0];
3. while循环后:min=1,k=2 打印(0,2),然后lowcost[2]=0;
4. 第二个for循环后:lowcost=[0,0,0,3],adjvex=[0,0,0,2];
5. while循环后:min=3,k=3 打印(2,3),然后lowcost[3]=0;
6. 第二个for循环后:lowcost=[0,0,0,0],adjvex=[0,0,0,3]
7. 至此,所有顶点全部访问过,lowcost中的数组全为0,最终退出第一个for循环。
我们将图的代码和普利姆算法的全部代码给出:
public class MyGraph{
public static final int INFINITY = 65535; //用65535表示∞
private String[] vertexdata; //顶点信息数组
private int[][] arc; //邻接矩阵表
private int w; //权值
private int vertexs; //顶点数
private int edges; //边数
public MyGraph(){
this.vertexs = 0;
this.edges = 0;
}
//创建并存储一个无向图(邻接矩阵)
public void CreateMyGraph(){
Scanner scanner = new Scanner(System.in);
System.out.print("请输入顶点数和边数:");
this.vertexs = scanner.nextInt();
this.edges = scanner.nextInt();
arc = new int[vertexs][vertexs];
vertexdata = new String[vertexs];
visited = new boolean[vertexs];
edge = new Edges[this.edges];
for(int i=0;i<vertexs;i++){
for(int j=0;j<vertexs;j++){
arc[i][j] = INFINITY;
}
}
for(int i=0;i<this.edges;i++){
edge[i] = new Edges();
}
System.out.print("请输入顶点信息:");
for(int i=0;i<this.vertexs;i++){
vertexdata[i] = scanner.next();
}
for(int i=0;i<this.edges;i++){
System.out.print("请输入边(vi,vj)上的下标i,下标j和权w:");
int v1 = scanner.nextInt();
int v2 = scanner.nextInt();
this.w = scanner.nextInt();
arc[v1][v2] = this.w;
arc[v2][v1] = arc[v1][v2];
edge[i].begin = v1;
edge[i].end = v2;
edge[i].weight = w;
}
}
//最小生成树--普里姆算法
/*对图的顶点进行操作“加点法”
这种算法需要创建两个数组,adjvex和lowcost,它们分别是存储相关顶点下标和邻接矩阵中某行的权值
利用这两个数组,对邻接矩阵进行遍历,然后寻找权值小的顶点,最后将最小权值的顶点打印。其中,
lowcost数组中如果数值有0,则说明给顶点已经被“访问”。
*/
public void MinSpanTree_Prim(){
int i,j,min;
int k;
int[] adjvex = new int[this.vertexs];//存储相关顶点下标
int[] lowcost = new int[this.vertexs];//存储邻接矩阵的其中一行
lowcost[0] = 0;//将第一个顶点v0加入生成树
adjvex[0] = 0;//初始化第一个顶点下标为0
for(i=1;i<this.vertexs;i++){//初始化
lowcost[i] = arc[0][i];//将邻接矩阵的第一行赋予lowcost
adjvex[i] = 0;//初始化都为v0的下标0
}
System.out.println("Prim的最小生成树为:");
for(i=1;i<this.vertexs;i++){
min = INFINITY;
j = 1;
k = 0;
//比较出在lowcost中顶点间边的权值的最小值min,然后记录当前最小值的下标k
while(j<this.vertexs){
if(lowcost[j]<min&&lowcost[j]!=0){
min = lowcost[j];
k = j;
}
j++;
}
System.out.println("("+adjvex[k]+","+k+")"+" 权值:"+lowcost[k]);
lowcost[k] = 0;//将已经访问过的顶点置为0
//因为已经访问过下标为k的顶点,所以继续遍历与下标为k的顶点相连接的顶点
for(j=1;j<this.vertexs;j++){
//判断相连接的顶点间的权值是否为0且它们间的权值是否小于此前顶点间的权值
if(lowcost[j]!=0&&lowcost[j]>arc[k][j]){
lowcost[j] = arc[k][j];//将更小的权值赋予lowcost中
adjvex[j] = k;//将下标为k的顶点存入adjvex
}
}
//此轮for循环结束后,进入上层for循环,继续遍历寻找最小的权值,并打印相连接顶点
//直到所有的顶点遍历完
}
}
}
我们进行用例测试: