普里姆算法(Prim)与最小生成树问题
普里姆算法
@anthor:QYX
普里姆算法在找最小生成树时,将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 A 类),剩下的是另一类(假设为 B 类)。
对于给定的连通网,起始状态全部顶点都归为 B 类。在找最小生成树时,选定任意一个顶点作为起始点,并将之从 B 类移至 A 类;然后找出 B 类中到 A 类中的顶点之间权值最小的顶点,将之从 B 类移至 A 类,如此重复,直到 B 类中没有顶点为止。所走过的顶点和边就是该连通图的最小生成树。
例如,通过普里姆算法查找图 2(a)的最小生成树的步骤为:
假如从顶点A出发,顶点 B、C、D 到顶点 A 的权值分别为 2、4、2,所以,对于顶点 A 来说,顶点 B 和顶点 D 到 A 的权值最小,假设先找到的顶点 B:
继续分析顶点 C 和 D,顶点 C 到 B 的权值为 3,到 A 的权值为 4;顶点 D 到 A 的权值为 2,到 B 的权值为无穷大(如果之间没有直接通路,设定权值为无穷大)。所以顶点 D 到 A 的权值最小:
最后,只剩下顶点 C,到 A 的权值为 4,到 B 的权值和到 D 的权值一样大,为 3。所以该连通图有两个最小生成树:
另一个例子:
package com.qyx.Tree; import java.util.Arrays; /** * prim算法最小生成树解决修路问题 * @author Administrator * */ public class PrimAlgorithm { public static void main(String[] args) { //测试图是否创建成功 char[] data=new char[]{'A','B','C','D','E','F','G'}; int verxs=data.length; //邻接矩阵使用二维数组表示 int[][] weight=new int[][]{ //A B C D E F G {10000,5,7,10000,10000,10000,2}, //A {5,10000,10000,9,10000,10000,3}, //B {7,10000,10000,10000,8,10000,10000}, //C {10000,9,10000,10000,10000,4,10000}, //D {10000,10000,8,10000,10000,5,4}, //E {10000,10000,10000,4,5,10000,6}, //F {2,3,10000,10000,4,6,10000}, //G }; MGraph mgraph=new MGraph(verxs); MinTree minTree=new MinTree(); minTree.createGraph(mgraph, verxs, data, weight); minTree.showGraph(mgraph); //测试普利姆算法 minTree.prim(mgraph, 0); } } //创建最小生成树->村庄的图 class MinTree{ //创建图的邻接矩阵 /** * * @param graph 图对象 * @param verxs 图对应的顶点个数 * @param data 图的各个顶点的值 * @param weight 图的领结矩阵 */ public void createGraph(MGraph graph,int verxs,char data[],int[][] weight) { int i,j; for(i=0;i<verxs;i++) //定点 { graph.data[i]=data[i]; for(j=0;j<verxs;j++) { graph.weight[i][j]=weight[i][j]; } } } //显示图的邻接矩阵 public void showGraph(MGraph graph) { for(int[] link:graph.weight) { System.out.println(Arrays.toString(link)); } } //编写prim算法,得到最小生成树 /** * * @param graph 图 * @param v 表示从图的第几个顶点开始生成 */ public void prim(MGraph graph,int v) { //标记节点是否被访问过 boolean visited[]=new boolean[graph.verx]; for(int i=0;i<graph.verx;i++) { visited[i]=false; } //把当前这个节点标记为已访问 visited[v]=true; //h1和h2记录两个顶点的下标 int h1=-1; int h2=-1; int minWeight=10000;//将其先初始为一个大数,后面在遍历过程中会被替换 //确定每一次生成的子图和哪个节点距离最近 for(int k=1;k<graph.verx;k++){ //在n个节点下,会有n-1条边 for(int i=0;i<graph.verx;i++) //i表示被访问过的结点 { for(int j=0;j<graph.verx;j++) //j结点表示还没有访问过的结点 { if(visited[i]==true&&visited[j]==false&&graph.weight[i][j]<minWeight) { //替换minWeight(寻找已经访问过的结点和未访问过的结点间的权值最小的边) minWeight=graph.weight[i][j]; h1=i; h2=j; } } } //找了一条边是最小的,将当前这个结点标记为已经访问 System.out.println(h1+"->"+h2+"权值为"+minWeight); visited[h2]=true; //minWeight重新设置为最大值 minWeight=10000; } } } class MGraph{ int verx;//表示图的节点个数 char[] data;//存放节点的数据 int[][] weight;//存放边,就是我们的邻接矩阵 public MGraph(int verx) { this.verx=verx; data=new char[verx]; weight=new int[verx][verx]; } }