最小生成树问题
普里姆算法要解决的就是最小生成树问题(MinimumCostSpanningTree),,简称MST。
给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树
最小生成树的特点:
1)N个顶点就一定有N-1条边
2)包含全部的顶点
普利姆算法
普利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,
找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图
问题例子
现在有7个村庄(A,B,C,D,E,F,G),现在需要修路把7个村庄连通
各个村庄的距离用边线表示(权),比如A–B距离5公里
如何修路保证各个村庄都能连通,并且总的修建公路总里程最短?
解决
prim算法是同图的随便一个节点进行开始,无论从哪一个节点开始,最终生成的最小生成树的权重都是一样的,那么现在就从A开始进行处理
第一步
首先标记A节点已经被访问过 visited=[A]
然后看和A相连结的还没有被访问过的节点,那么现在就有
A-C(7),A-G(2),A-B(5),其中2是最小的,就连接A-G,
然后标记G已经被访问过,visited=[A,G]
第二步
现在A和G已经被访问过了,将AG看作一个整体,也是找和AG相连的还没被访问过的节点,那么现在有
A-C(7),G-E(4),G-F(6),G-B(3),A-B(5),其中3是最小的,连接G-B
然后标记B已经被访问过,visited=[A,G,B]
然后
一直这样循环下去,直到找出了n-1条边,就是找出6条边
代码实现
package basic;
import java.util.Arrays;
public class Prim
{
public static int [][]weight=new int[][]{
{
10000,5,7,10000,10000,10000,2},
{
5,10000,10000,9,10000,10000,3},
{
7,10000,10000,10000,8,10000,10000},
{
10000,9,10000,10000,10000,4,10000},
{
10000,10000,8,10000,10000,5,4},
{
10000,10000,10000,4,5,10000,6},
{
2,3,10000,10000,4,6,10000},};
public static char[] nodes=new char[] {
'A','B','C','D','E','F','G'};
public static int number=nodes.length;
public static Graph graph=new Graph(number);
public static void main(String[] args)
{
CreateGraph(graph, number, nodes, weight);
show(graph);
prim(graph, 0);
}
/*
* 普利姆算法的核心,传入的参数分别是图graph和从哪一个节点开始index
* 首先创建一个标记节点是否被访问过的数组visited
* 然后将传进来的index节点标记为已经被访问过,还要定义一个数值很大的MinWeight
* 用来寻找图中小的权值的边
* 然后是for循环,我们需要生成graph.number-1条边
* 所以第一重for循环只是循环需要生成的边
* 然后再是两层的for循环,用来遍历整个图的,寻找最小的还没访问的边
*/
public static void prim(Graph graph,int index)
{
boolean[] visited=new boolean[graph.number];
visited[index]=true;
int MinWeight=Integer.MAX_VALUE;
int x1=0,x2=0;
for(int x=0;x<graph.number-1;x++)
{
for(int i=0;i<graph.number;i++)
{
for(int j=0;j<graph.number;j++)
{
if(visited[i]&&!visited[j]&&graph.weight[i][j]<MinWeight)
{
MinWeight=graph.weight[i][j];
x1=i;
x2=j;
}
}
}
System.out.println("边:" + graph.nodes[x1] + "-" + graph.nodes[x2] + ",权值:" + MinWeight);
visited[x2]=true;
MinWeight=Integer.MAX_VALUE;
}
}
/**
* 这是初始化对应的图对象,创建好对应的邻接矩阵
* @param graph 图对象
* @param number 节点数
* @param nodes 保存节点的数组
* @param weight 保存权重的邻接矩阵
*/
public static void CreateGraph(Graph graph,int number,char[] nodes,int[][] weight)
{
for(int x=0;x<number;x++)
{
graph.nodes[x]=nodes[x];
for(int y=0;y<number;y++)
{
graph.weight[x][y]=weight[x][y];
}
}
}
public static void show(Graph graph)
{
for(int[] temp:graph.weight)
{
System.out.println(Arrays.toString(temp));
}
}
}
/*
* 首先创建图对象,包含图的节点个数的number
* 保存每一个节点的数组nodes,保存边的权重的邻接矩阵weight
*/
class Graph
{
int number;
char[] nodes;
int[][] weight;
public Graph(int number)
{
this.number = number;
this.nodes=new char[number];
this.weight=new int[number][number];
}
}