一、什么是最小生成树
最小生成树是具有如下特征的联通图
1)包含图中的所有顶点
2)任意顶点之间有且仅有一条通路
3)边的权值总合最小
示例:
上图中最小生成树为 a->b->c->e->d
二、最小生成树的应用
1)使所有道路联通,怎样花费最小
2)铺设光缆,怎样花费最小
3)多国旅游,怎样花费最小
........
总结:适用于解决减少成本花费的问题
三、prim(普里姆算法)
基本思想:
1)将所有顶点分为两类,A(选中状态)类和B(未被选中状态)类,初始时所有顶点位于B类
2)选择任意一个B类中的顶点将其移动到A类
3)从B类中的所有顶点中找出一条权值最小的连接A类顶点的边,将该B类的顶点移动到A类
4)重复3)过程,直至找到 顶点数-1 条边
四、实现的思路与方法
prim算法是一种思想,实现的具体方法有很多种,我的实现方法如下。
1)图的存储结构有好几种,这里我采用的是邻接矩阵存储方法
注意:在带权的联通图中正无穷需要我们自已定义一个数来表示,或采用该数据类型的最大值。
2)如何记录最小生成树的路径
这里采用数组(parent[])记录最小生成树的路径,数组下标表示顶点,数组的值表示该顶点的父结点,第一个结点的父结点置为-1。
3)如何区分A类与B类
这里我采用一个布尔数组(visited[])表示,true代表被选中为A类,false代表未被选中为B类。
4)将B类移动A类的具体过程
//将第一个顶点加入A类
parent[0] = -1
visited[0] = true
//将剩余n-1个顶点加入到A类
for(i<n-1){
//遍历邻接矩阵找B类到A类中权值最小的点
for(i<n){
if(){//如果是A类顶点
for(){//找出当前B类到A类权值最小的点
}
}
}
}
五、具体的实现代码和注释解析
public class MiniSpanTree_Prim {
/*
int[][] g:其中i为图的顶点,j为与该顶点相邻的顶点,相邻则tree[i][j] = 权值
规定权值最大为100,其中101视为无穷大
*/
public MiniSpanTree_Prim(int[][] graphics){
int[][] g = graphics;
//用于记录父节点的位置
int[] parent = new int[g.length];
//用于记录其到父节点的权值
int[] weight = new int[g.length];
//用于记录该顶点是否被选择
boolean[] visited = new boolean[g.length];
//初始化数组
for(int i = 0;i < g.length;i ++){
parent[i] = -1;
weight[i] = 101;
visited[i] = false;
}
//从第一个结点开始寻找最小生成树
weight[0] = 0;//没有父节点权值为0
visited[0] = true;//标记为已选
//只需要找到 顶点数-1 条边
for(int x = 0;x < g.length-1;x ++){
int i = -1;
int j = -1;
int w = 101;
//从已选中的结点中选出到下个结点权值最小的结点
for(int y = 0;y < g.length;y ++){
if(visited[y] == true){//若果是已选结点
for(int z = 0;z < g.length;z ++){
//如果当前权重小于最小权重且该结点未被选中,则更新最小权重
if(g[y][z] < w && visited[z] != true){
w = g[y][z];
i = y;
j = z;
}
}
}
}
//记录父结点
parent[j] = i;
//记录到父结点的权重
weight[j] = w;
//该点标记为已选
visited[j] = true;
}
//打印输出各结点的父结点
for(int tmp:parent){
System.out.println(tmp);
}
}
public static void main(String[] args) {
int[][] g = {
{0,1,2,4},
{1,0,3,101},
{2,3,0,1},
{4,101,1,0}
};
new MiniSpanTree_Prim(g);
}
}
6、测试结果