最小生成树包含n个顶点和(n-1)条边,并且边的权重最小。Prim算法的思想是:由一颗小树慢慢长大,首先分为两个顶点集合,最小生成树的顶点集合A,和不在生成树中的顶点集合B,每次从B中找一个顶点v,使其到A中的某个顶点的权重最小,并将v放入A,同时从B删除。以下图为例(图中的数字即为权重),进行简单的说明:
步骤如下(以v1为起点):
(1)一开始,集合A只有顶点v1,剩下的顶点在集合B中,即A={v1}、B={v2,v3,v4,v5,v6,v7}
(2)从B中找一个顶点v,使其到A中的某个顶点的权重最小,从图中可以看出,v4到v1的权重最小,所以将v4收入到最小生成树的集合中A,此时A={v1,v4}、B={v2,v3,v5,v6,v7}
(3)重复步骤(2),当前A={v1,v4}、B={v2,v3,v5,v6,v7},从图中看出,(v1,v2)=2,、(v3,v4)=2,两条边的权重一样大,都是最小的,那么我们既可以收入顶点v2,也可以收入顶点v3,那么我们按顶点顺序,先收入顶点v2,即此时A={v1,v2,v4}、B={v3,v5,v6,v7}
(4)重复步骤(2),由于顶点v3到顶点v4的权重最小,为2,故收入顶点v3到A中,此时A={v1,v4,v2,v3}、B={v5,v6,v7}
(5)重复步骤(2),由于(v1,v3)=4,、(v4,v7)=4的权重都是最小的,但是顶点v3已经在集合A中了,故不能收入,否则就会出现环,所以应该收入顶点v7,即此时A={v1,v4,v2,v3,v7}、B={v5,v6}
(6)重复步骤(2),v7收入到集合A后,发现到顶点的v6的权重为1,是最小的了,故收入顶点v6到集合A中,即此时A={v1,v4,v2,v3,v7,v6}、B={v5}
(7)将顶点v5收入到集合A中,即此时A={v1,v4,v2,v3,v7,v6,v5}、B={},至此,所有顶点都已经收集完了,由这些顶点及收入顶点过程中边组成的,便是最小生成树了。
从以上步骤也可以看出,Prim算法就是一个收集顶点的过程,只不过它每次都收集权重最小的边的顶点,是一种贪心算法的思想。
Java代码实现:
(1)顶点结构:
//图的顶点类型
class Vertex {
public char value; //顶点值
public boolean visited; //顶点是否被访问过
public Vertex(char v) {
value=v;
visited=false;
}
}
(2)边的结构
//存储边的两个顶点及边的权值
class Edge{
public int u; //顶点
public int v; //顶点
public int w; //权值
public Edge(int u,int v,int w) {
this.u=u;
this.v=v;
this.w=w;
}
}
(3)prim算法实现
//Prim算法解决最小生成树问题,以顶点为思考对象
//Prim算法思想:由一颗小树慢慢长大,首先分为两个顶点集合,树的顶点集合A,和不在生成树中的顶点集合B,每次从B中找一个顶点v,
// 使其到A中的某个顶点的权重最小,并将v放入A,同时从B删除
public void prim(int start) {
int[] prims=new int[vertexList.length]; //记录最小生成树的顶点序号
//初始化
for(int i=0;i<vertexList.length;i++) {
distance[i]=mGraph[start][i]; //到起点的权值
prims[i]=-1;
}
distance[start]=0; //自己到自己的距离为0
int index=0; //最小生成树的索引
prims[index++]=start;
for(int i=0;i<vertexList.length;i++) {
if(i==start) {
continue;
}
int min=Integer.MAX_VALUE;
int k=-1;
for(int j=0;j<vertexList.length;j++) {
//distance[j]==0,表示已经在最小生成树中
//如果不在最小生成树中,并且与最小生成树中的某个顶点组成的边的权值更小
if(distance[j]!=0 && distance[j]<min) {
min=distance[j];
k=j;
}
}
//循环结束后,第k个顶点就是和已经收录的顶点构成边权值最小的顶点
prims[index++]=k;
distance[k]=0; //到最小生成树的距离为0,表示已经在最小生成树中
//更新第k个顶点到未被收录进最小生成树中邻接点的权值
for(int j=0;j<vertexList.length;j++) {
//如果j是k的未被收录的邻接点,mGraph[k][j],是顶点k到顶点j的距离
if(distance[j]!=0 && mGraph[k][j] < distance[j]) {
distance[j]=mGraph[k][j]; // 更新顶点j到生成树的距离
}
}
}
//打印最小生成树
for(int i=0;i<vertexList.length;i++) {
System.out.print(vertexList[prims[i]].value+" ");
}
//打印最小权值:从最小生成树的第二个顶点开始,找它到前驱顶点的最小权值
int sum=0;
//一共n-1条边
for(int i=1;i<vertexList.length;i++) {
int min=Integer.MAX_VALUE;
for(int j=0;j<i;j++) {
if(mGraph[prims[j]][prims[i]]<min) {
//prims[j]表示已经在最小生成树中的顶点
min=mGraph[prims[j]][prims[i]];
}
}
sum+=min;
}
System.out.println("最小权值和为:"+sum);
}
完整代码,请参考:https://blog.csdn.net/qiuxinfa123/article/details/83719789