- 举个最小生成树的例子:
在一个地区有很多个小村庄,并且每个村之间都可以修路进行连通。实际上,每条路由于地形不一样,所需的经费不同,并且不可能每两个村庄都直接互通,可以间接互通,从而降低经费。如何实现呢?这时候最小生成树的作用就体现出来了。 - 如下图:(每个点代表一个村庄,边上的值代表修路所需经费)
生成最小生成树的方法最经典的有两种:普利姆算法和克鲁斯卡尔算法 。下面我们来说说普利姆算法,普利姆算法又叫加点法。 - 通过普利姆算法最小生成树的要点主要简单归纳为如下三点:
1)任意选择一个点作为起点
2) 在未连接的点集中选择与已连接部分的路径最短的点
3)不能形成封闭回路
以C为起点为例:
1)找到C点
2)在未连接的点集中选择与已连接部分(C点)的路径最短的点:可以看出是G点,将他们连接起来。
3)继续第二步,直到将所有点直接或者连通在一起:在未连接的点集中选择与已连接部分(C, G点)的路径最短的点:可以看出是G点,将他们连接起来。可以看出是B点,将他们连接起来。
以此进行下去 最后结果如下:也就是最小生成树了。
上面就是普利姆算法的基本思路。如何用代码实现呢,往下看。
-
代码实现
要记录一个图,我们在代码中就要记录点还有点之间的边,所有我们需要一个点集和一个边集。
点集我们可以通过一个Set集合来充当
边集合我们可以通过一个二维数组来建立: 如下图,表示点与点之间的距离,数组的索引对应MapNode中的index值。
- 封装一个图中所需的点
public class MapNode {
public String val;
public List<MapNode> neighbor;//用来记录最终点周围的连接情况
public int index;//用于绑定图中的边集
public MapNode(String val, int index) {
this.val = val;
this.index = index;
neighbor = new ArrayList<>();
}
}
- 普利姆算法的核心代码
//普利姆算法
public static void prim(Set<MapNode> pointSet, int[][] distance, MapNode startPoint){
Set<MapNode> rs = new HashSet<>();
rs.add(startPoint);
while (rs.size() < pointSet.size()){
getMinDis(pointSet,distance,rs);
}
}
//找到距离已有的点最近的点
public static void getMinDis(Set<MapNode> pointSet, int[][] distance, Set<MapNode> rs){
MapNode start = null;
MapNode end = null;
int minDis = Integer.MAX_VALUE;
for(MapNode temp : rs){
int idx = temp.index;
for(int i = 0; i < distance[temp.index].length; i ++){//根据当前点索引到边的二维数组中找到该点到每个点的距离的一个一维数组,进行判断
if(distance[idx][i] < minDis //找到距离最近的点
&& i != idx //当前点无需在自己比较距离
&& !rs.contains(getMapNodeByInx(pointSet,i))
){
start = temp;
end = getMapNodeByInx(pointSet,i);
minDis = distance[idx][i];
}
}
}
//将距离最短的两个点连接在一起
start.neighbor.add(end);
end.neighbor.add(start);
System.out.println(start.val+"----->"+end.val);
rs.add(end);
}
//根据索引找到相应的点
public static MapNode getMapNodeByInx(Set<MapNode> pointSet, int index){
for(MapNode node : pointSet)if (node.index == index) return node;
return null;
}
- 记录上面所举例的图并且通过普利姆算法最终生成最小生成树
public class Main {
public static void main(String[] args) {
//将图中的点构建出来
MapNode pointA = new MapNode("A",0);
MapNode pointB = new MapNode("B",1);
MapNode pointC = new MapNode("C",2);
MapNode pointD = new MapNode("D",3);
MapNode pointE = new MapNode("E",4);
MapNode pointF = new MapNode("F",5);
MapNode pointG = new MapNode("G",6);
int max = Integer.MAX_VALUE;
//图中每个点到图中点的距离(每条边的权值)//边集合
int[][] distance = new int[][]{
{0,5,max,13,max,max,8},
{5,0,6,max,max,max,10},
{max,6,0,max,max,8,4},
{13,max,max,0,9,max,11},
{max,max,max,9,0,9,7},
{max,max,8,max,9,0,20},
{8,10,4,11,7,20,0}
};
//点集
Set<MapNode> pointSet = new HashSet<>();
pointSet.add(pointA);
pointSet.add(pointB);
pointSet.add(pointC);
pointSet.add(pointD);
pointSet.add(pointE);
pointSet.add(pointF);
pointSet.add(pointG);
prim(pointSet,distance,pointC);//执行之后就可以查看生成的结果了
System.out.println("生成完毕")// debug一下
}
- 生成结果:
暂且不全部展开,可以看到,和我们上面生成的最小生成树结果是一样的。