最近复习到了数据结构中图的遍历和最小生成树部分,我在这里记录用java写的prim算法,方便自己回顾,并且与他人交流学习。
算法简介
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小,普里姆算法是基于贪心策略的一种算法,他将按照逐个顶点连通的方式构造最小生成树
算法思想
通过例题来陈述思想,
代码思路:
1.构造 lowcost:最小代价的权值数组,此数组的作用就是找最小的权值
构造 adjvex:存放已经找到最小权值的权重和起点,比如adjvex[1] = 9,表示在最小生成树中有一条起点为v1的权值为9的边。
2.初始化lowcast数组,把起始点到每个顶点权值给他,此题中也就是邻接矩阵的第一行。
3.找邻接矩阵中的最小值,此题中应该是10,是v0->v1的权值,min(最小值)为10,minId(最小值权值的终点)为v1,将lowcast[minid] = 0,意思就是v0到v1已经找到最小权,不在参与lowcast的更新。将adjvex[1] = 10,在最小生成树中有v0->v1权值为10.
4.更新lowcast:将邻接矩阵中的v1那一行和lowcast做比较,取最小值替换掉lowcast中的值。
5.不断重复步骤3和步骤4,顶点有多少个就循环顶点次数减一次,也可以是lowcast中的所有值为都为0
在代码中也有相应的注释,可以配合注释和最后手写的解题步骤更容易理解。
代码实例
/***
* 使用普里姆算法生成最小生成树
* @author 65481
*
*/
public class PrimGraph {
//顶点数量
private int vertexSize;
//临接矩阵
private int [][] matrix;
//存放顶点的数组
private int [] vertexs;
//表示顶点之间没有关系
private static int MAX_WIGHT = 1000;
//表示顶点是否被访问过
private boolean [] isvisited;
//构造函数
public PrimGraph(int vertexSize) {
this.vertexSize = vertexSize;
matrix = new int[vertexSize][vertexSize];
vertexs = new int[vertexSize];
for(int i = 0;i < vertexSize;i ++) {
vertexs[i] = i;
}
isvisited = new boolean[vertexSize];
}
/**
* 普里姆算法
*/
public void prim() {
//最小代价顶点权值的数组,0表示已经是最小的了
int [] lowcost = new int[vertexSize];
//存放已经比较过后的顶点权值
int [] adjvex = new int [vertexSize];
//min表示这一行最小的那条边,minId表示最小那条边的下标 ,权值总和
int min,minId,sum = 0;
//把邻接矩阵的第一行放入到最小代价顶点权值数组
for(int i = 1;i < vertexSize;i ++) {
lowcost[i] = matrix[0][i];
}
//在lowcost中找到最小的权值并且记录权值大小到adjvex对应的位置中,记录最小值min,记录最小那条边的下标minId
for(int i = 1;i < vertexSize ;i ++) {
//初始化
min = MAX_WIGHT;
minId = 0 ;
for(int j = 1 ;j <vertexSize ; j++) {
if(lowcost[j] > 0 &&lowcost[j] < min) {
min = lowcost[j];
minId = j;
}
}
System.out.println("顶点:"+adjvex[minId]+"权值:"+min);
sum+=min;
lowcost[minId] = 0;
for(int j = 1;j < vertexSize;j ++) {
//更新lowcost
if (lowcost[j] !=0 && lowcost[j] > matrix[minId][j]) {
lowcost[j] = matrix[minId][j];
adjvex[j] = minId;
}
}
}
System.out.println("最小生成树权值和为:"+sum);
}
public static void main(String []args) {
PrimGraph primGraph = new PrimGraph(9);
int [] a0 = new int[]{0,10,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,11,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT};
int [] a1 = new int[]{10,0,18,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,16,MAX_WIGHT,12};
int [] a2 = new int[]{MAX_WIGHT,MAX_WIGHT,0,22,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,8};
int [] a3 = new int[]{MAX_WIGHT,MAX_WIGHT,22,0,20,MAX_WIGHT,MAX_WIGHT,16,21};
int [] a4 = new int[]{MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,20,0,26,MAX_WIGHT,7,MAX_WIGHT};
int [] a5 = new int[]{11,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,26,0,17,MAX_WIGHT,MAX_WIGHT};
int [] a6 = new int[]{MAX_WIGHT,16,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,17,0,19,MAX_WIGHT};
int [] a7 = new int[]{MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,16,7,MAX_WIGHT,19,0,MAX_WIGHT};
int [] a8 = new int[]{MAX_WIGHT,12,8,21,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,MAX_WIGHT,0};
primGraph.matrix[0] = a0;
primGraph.matrix[1] = a1;
primGraph.matrix[2] = a2;
primGraph.matrix[3] = a3;
primGraph.matrix[4] = a4;
primGraph.matrix[5] = a5;
primGraph.matrix[6] = a6;
primGraph.matrix[7] = a7;
primGraph.matrix[8] = a8;
primGraph.prim();
}
算法结果
顶点:0权值:10
顶点:0权值:11
顶点:1权值:12
顶点:8权值:8
顶点:1权值:16
顶点:6权值:19
顶点:7权值:7
顶点:7权值:16
最小生成树权值和为:99
帮助理解
这里有一些不好理解的地方:
有关于for循环如代码所示,v0和v1有最小的权值为10,下面应该从v1开始,排除v0去找最小的权值。
代码的实现思路是用临接矩阵中的第一行和lowcast数组中的值进行比较,把较小的放入lowcast,然后开始第二次for循环(第二次就相当于从v1开始),动手写一写有助于开导思路(只写了前两次循环):