利用优先队列PriorityQueue实现Prim算法

1.Prim算法介绍

1.Prim算法是贪婪技术的一种。

2.贪婪技术:
    在每一步中,都‘贪婪’地选择最佳的操作,并希望通过一些列局部最优选择,能够产生一个整个问题(全局)的最优解。

3.Prim算法的用处:
    通俗的讲就是求解最小生成树问题。

4.Prim算法思想(贪婪的体现):
    Prim算法将顶点分为2个集合vt和v-vt,并总是找出边(a,b)使得a∈vt,b∈v-vt,且边的权重最小。

5.Prim算法的关键之处:
    Prim算法的关键之处在于求出边(a,b)使得a∈vt,b∈v-vt,且边的权重最小。此处求权重最小的边可以用优先队列PriorityQueuen来求取。

2.代码实现

package algorithm.experiment6;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Prim 
{
    //表示无穷大
    final static Integer M = Integer.MAX_VALUE;
    static int[][] G = {
            {M,3,M,M,6,5},
            {3,M,1,M,M,4},
            {M,1,M,6,M,4},
            {M,M,6,M,8,5},
            {6,M,M,8,M,2},
            {5,4,4,5,2,M}};
    //全部顶点名称【对应数组的下标】
    static char[] vName = {'a','b','c','d','e','f'};
    //顶点是否被选【对应数组的下标】
    static boolean[] vIsChoose = {true,false,false,false,false,false};


    public static void main(String[] args)
    {
        prim(G);
    }

    public static void prim(int[][] G)
    {

        System.out.println("默认的点:a  集合vt:a");
        while(true)
        {
            //判断是否生成了最小生成树
            if(isOver()) break;

            PriorityQueue<MyEdge> myPriorityQueue = myPriorityQueue(G,vIsChoose);

            MyEdge edge = myPriorityQueue.poll();
            changeG(edge);

//          System.out.print(edge+"    ");
            System.out.print("选择的点:"+vName[edge.index2]+"  集合vt:");

            for(int i=0;i<vIsChoose.length;i++)
                if(vIsChoose[i])
                    System.out.print(vName[i]+" ");
            System.out.println();
        }
    }

    //判断是否找到了最小生成树(判断数组vIsChoose如果全为true则返回true)
    public static boolean isOver()
    {
        for(Boolean b:vIsChoose){
            if(!b) return false;
        }
        return true;
    }

    //改变图以及已经选择的标记数组
    private static void changeG(MyEdge edge)
    {
        int x = edge.index1;
        int y = edge.index2;

        G[x][y] = M;
        G[y][x] = M;
        vIsChoose[x] = true;
        vIsChoose[y] = true;
    }

    //构造一个myEdge的优先队列
    public static PriorityQueue<MyEdge> myPriorityQueue(int[][] G,boolean[] vIsChoose)
    {
        PriorityQueue<MyEdge> myPriorityQueue = new PriorityQueue<MyEdge>(11,myEdgeComparator());

        for(int i=0;i<vIsChoose.length;i++)
        {
            //如果顶点被选则找出相邻的顶点并组成边加入到优先队列中
            if(vIsChoose[i])
            {
                //遍历相应的顶点
                for(int j=0;j<G[i].length;j++)
                {
                    //将相邻的顶点组成的边加入优先队列 且该边的连个顶点是包含于vt和v-vt的
                    if(G[i][j]!=M&&!vIsChoose[j])
                    {
                        MyEdge edge = new MyEdge(i, j, G[i][j]);
                        myPriorityQueue.offer(edge);
                    }
                }
            }
        }
        return myPriorityQueue;
    }

    //表示边 index1:表示顶点1的在字符数组中的下标
    static class MyEdge
    {
        public int index1,index2;
        public int weight;

        public MyEdge(int v1,int v2,int weight){
            this.index1 = v1;
            this.index2 = v2;
            this.weight = weight;
        }

        public String toString(){
            return "("+vName[index1]+","+vName[index2]+","+weight+")";
        }
    }

    //myEdge比较器
    public static Comparator<MyEdge> myEdgeComparator()
    {
        Comparator<MyEdge> comparator = new Comparator<MyEdge>(){
            @Override
            public int compare(MyEdge o1, MyEdge o2)
            {
                return o1.weight-o2.weight;
            }
        };
        return comparator;
    }

}

3.运行结果

默认的点:a  集合vt:a
选择的点:b  集合vt:a b 
选择的点:c  集合vt:a b c 
选择的点:f  集合vt:a b c f 
选择的点:e  集合vt:a b c e f 
选择的点:d  集合vt:a b c d e f 

4.代码分析

    首先是图的表示,我采用的是邻接矩阵的方式来表示图,用vName的字符数组表示顶点名称,用vIsChoose表示相应的坐标是否被选择。
    内部类MyEdge用于表示边,在myPriorityQueue(int[][] G,boolean[] vIsChoose)方法中首先构建了一个优先队列,该队列通过自己实现的比较器(方法myEdgeComparator()实现)每次出队都可以得到一个满足条件的边(a,b)(权重最小,a∈vt,b∈v-vt)。依次循环直到找到最小生成树从而退出循环。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值