Prim算法实现最小生成树MST(java)

Prim算法是另一种生成图的最小生成树的算法,这里简单说一下Prim算法和Kruskal算法的在实现方面的区别:

1、Kruskal算法在生成最小生成树的过程中产生的是森林,Prim算法在执行过程中始终都是一棵树;

2、Kruskal和Prim实现上的最大区别是Kruskal不需要搜索每个顶点的邻接节点,而Prim中需要,所以Prim图构建时需要利用邻接链表进行构建,Kruskal不用!

上面第二点边提出了Prim算法实现过程中特点,就是构建图的过程中需要邻接链表表示加权无向图!
Prim算法中构建的加权无向图的邻接链表如下图所示:

这里写图片描述

根据上图可知,即是将权重作为邻接链表中的每个节点对象的属性。
比如上面第一个邻接链表表示的便是:边v1–>v8:权重为8;边v1–>v2:权重为4


一、Prim算法需要的数据结构知识

1、图顶点对象及其属性定义:

public class VertexWG {
    String verName;  //名称
    int weight;  //权重
    double key;  //key是键顶点v与树中某一顶点相连的边中最小权值
    VertexWG parent;  //父对象
    VertexWG nextNode;  //下一个节点对象
}

2、最小堆:最小堆的顶点对象是Vertex,排序是通过Vertex的属vertex.key进行排序的
对于vertex的key属性要明确:vertex.key是所有将v与树中某一顶点相连的边中最小权值,按约定如果不存在这样的边,则vertex.key=∞


二、Prim算法实现具体思路

首先给出伪代码:

这里写图片描述

伪代码1~5行:对每个顶点对象进行初始化,并初始化优先级队列Q,是其包含所有初始化的节点;
伪代码6~11行:首先只要优先级队列不为空,则while循环一直继续!从Q中选出v.key最小的顶点,然后遍历它的每一个邻接节点,当其邻接节点满足两个条件:
1、该节点v仍在Q中
2、该节点v满足w(u,v)< v.key
满足上面条件边更新节点的key值信息和parent信息。知道while循环结束!


三、Prim算法java实现

1、Graph对象类

public class GraphWG {
    VertexWG[] vertexArray=new VertexWG[100];
    int verNum=0;
    int edgeNum=0;
}

2、Vertex对象类

public class VertexWG {
    String verName;
    int weight;
    double key;
    VertexWG parent;
    VertexWG nextNode;
}

3、创建图

import java.util.Scanner;

public class CreateWG {

    double infinity=10.0/0.0;

    /**
     * 根据用户输入的string类型的顶点返回该顶点
     * @param graph 图
     * @param str 输入数据
     * @return返回一个顶点
     */
    public static VertexWG getVertex(GraphWG graph,String str){
        for(int i=0;i<graph.verNum;i++){
            if(graph.vertexArray[i].verName.equals(str)){
                return graph.vertexArray[i];
            }
        }
        return null;
    }

    /**
     * 初始化一个无向带权图,并且每个顶点包含parent域和key域
     * @param graph 生成的图
     */
    public void initialWg(GraphWG graph){
        @SuppressWarnings("resource")
        Scanner scan=new Scanner(System.in);
        System.out.println("请输入顶点数和边数:");
        graph.verNum=scan.nextInt();
        graph.edgeNum=scan.nextInt();

        System.out.println("请依次输入定点名称:");
        for(int i=0;i<graph.verNum;i++){
            VertexWG vertex=new VertexWG();
            String name=scan.next();
            vertex.verName=name;
            vertex.key=infinity;
            vertex.parent=null;
            vertex.nextNode=null;
            graph.vertexArray[i]=vertex;
        }



        System.out.println("请依次输入图的便边:");
        for(int i=0;i<graph.edgeNum;i++){
            String preV=scan.next();
            String folV=scan.next();
            int weight=scan.nextInt();

            VertexWG v1=getVertex(graph,preV);
            if(v1==null)
                System.out.println("输入边存在图中没有的顶点!");
            VertexWG v2=new VertexWG();
            v2.verName=folV;
            v2.weight=weight;
            v2.nextNode=v1.nextNode;
            v1.nextNode=v2;

            VertexWG reV2=getVertex(graph,folV);
            if(reV2==null)
                System.out.println("输入边存在图中没有的顶点!");
            VertexWG reV1=new VertexWG();
            reV1.verName=preV;
            reV1.weight=weight;
            reV1.nextNode=reV2.nextNode;
            reV2.nextNode=reV1;
        }
    }

    public void outputWG(GraphWG graph){
        System.out.println("输出加权图的邻接链表:");
        for(int i=0;i<graph.verNum;i++){
            VertexWG vertex=graph.vertexArray[i];
            System.out.print(vertex.verName);

            VertexWG current=vertex.nextNode;
            while(current!=null){
                System.out.print("-->"+current.verName+"("+current.weight+")");
                current=current.nextNode;
            }
            System.out.println();
        }
    }
}

4、Prim算法核心实现类

import java.util.Scanner;
/**
 * <pre>
 * 1>.Kruskal算法实现利用EdgeNode数据结构
 *    Kruskal和Prim实现上的最大区别是Kruskal不需要搜索每个顶点的邻接节点
 *    而Prim中需要,所以Prim图构建时需要利用邻接链表进行构建,Kruskal不用!
 * 2>.使用邻接链表构建无向加权图,prim构建MST过程中始终只有一棵树
 * 3>.每个vertex对象包含属性:parent、key、verName、weight、nextNode
 * 4>.这里最小堆minHeap节点对象是Vertex,构建是通过vertex.key构建的
 * 5>.root节点随机选取,通过遍历每个选出节点的邻接节点,然后不断更新每个节点的key值 !</pre>
 * @author King
 * 
 */
public class Prim {

//  下面全局变量对于程序十分重要
    int currentSize=0;
    int maxSize=0;
    VertexWG[] minHeap=new VertexWG[20];

    /**
     * 通过weight构建以EdgeNode为节点的最小堆
     * @param edgeNode为带权的边集
     */
    public void createMinHeap(VertexWG[] verArray){
        currentSize=verArray.length;
        maxSize=minHeap.length;
        if(currentSize>=maxSize){
            maxSize*=2;
            minHeap=new VertexWG[maxSize];
        }
        for(int i=0;i<currentSize;i++)
            minHeap[i+1]=verArray[i];

        double y;
        int c;
        for(int i=currentSize/2;i>=1;i--){
            VertexWG ver=minHeap[i];
            y=ver.key;
            c=2*i;
            while(c<=currentSize){
                if(c<currentSize && minHeap[c].key>minHeap[c+1].key)
                    c++;
                if(minHeap[c].key>=y)
                    break;
                minHeap[c/2]=minHeap[c];
                c=c*2;
            }
            minHeap[c/2]=ver;
        }
    }

    /**
     * 最小堆删除两种思路,一种和前面一样,就是一直跟踪放在根节点的那个最后一个节点最终插入的位置
     * 另一种思路便是每一次完成完整的交换然后下一一层在进行同样处理
     */
    public VertexWG deleteMinHeap(){
        if(currentSize<1)
            System.out.println("堆已经为空!无法执行删除");
        VertexWG ver=minHeap[1];
        minHeap[1]=minHeap[currentSize];
        currentSize-=1;

        int c=2,j=1;
        VertexWG ver1=minHeap[currentSize+1];
        while(c<=currentSize){
            if(c<currentSize && minHeap[c].key>minHeap[c+1].key)
                c++;
            if(ver1.key<=minHeap[c].key)
                break;
            minHeap[j]=minHeap[c];
            j=c;
            c=c*2;
        }
        minHeap[j]=ver1;
        return ver;
    }

    /**
     * 从这里返回minHeap中的顶点对象
     * @param name 定点的名字
     * @return返回minHeap中的一个顶点对象
     */
    public VertexWG getHeapVertex(String name){
        for(int i=1;i<=currentSize;i++){
            if(minHeap[i].verName.equals(name))
                return minHeap[i];
        }
        return null;
    }

    /**
     * MST的Prim算法具体实现函数
     * @param graph 图
     */
    public void primSpanningTree(GraphWG graph){
        System.out.println("请输入root节点:");
        @SuppressWarnings("resource")
        Scanner scan=new Scanner(System.in);
        String root=scan.next();
        VertexWG verRoot=CreateWG.getVertex(graph,root);
        verRoot.key=0;

        VertexWG[] verArray=new VertexWG[graph.verNum];
        for(int i=0;i<graph.verNum;i++){
            verArray[i]=graph.vertexArray[i];
        }
        createMinHeap(verArray);

        System.out.println("利用prim算法依次加入到MST中的顶点顺序为:");
        while(currentSize>=1){
//          需要考虑的一个问题便是minHeap中的对象和graph.vertexArray中的是否是同一个对象>>答案:不是同一个对象
//          minHeaph中的对象已经是新产生的对象了
//          这里注意一下u这个对象,u自然是minHeap中的对象
            VertexWG[] vArray=new VertexWG[currentSize];
            for(int i=0;i<currentSize;i++){
                vArray[i]=minHeap[i+1];
            }
//          这里需要注意,每次删除节点更新完key值之后,需要利用新的key值重新构建最小堆
            createMinHeap(vArray);

            VertexWG u=deleteMinHeap();
            System.out.println(">."+u.verName);
            VertexWG current=u.nextNode;
//          注意下面while循环中的两个不同的顶点对象:currentNow、current
            while(current!=null){
                VertexWG currentNow=getHeapVertex(current.verName);
                if(currentNow!=null && current.weight<currentNow.key){
                    currentNow.parent=u;
                    currentNow.key=current.weight;
                }
                current=current.nextNode;
            }
        }
    }

    public static void main(String[] args) {
        GraphWG graph=new GraphWG();
        CreateWG createWG=new CreateWG();
        createWG.initialWg(graph);
        createWG.outputWG(graph);
        Prim prim=new Prim();
        prim.primSpanningTree(graph);
    }
}

测试所有的无向加权图如下所示:

这里写图片描述

测试输入数据截图(部分):

这里写图片描述

测试结果截图:

这里写图片描述

这里大家可以看出Prim算法的根节点是自己随机选取的,不影响最小生成树的形成!

有什么问题欢迎大家指正,谢谢!!

猜你喜欢

转载自blog.csdn.net/feilong_csdn/article/details/69367374
今日推荐