普里姆算法(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];
    }
}

 

posted @ 2020-03-16 21:14  计算机的探索者  阅读(2548)  评论(0编辑  收藏  举报