Java数据结构与算法笔记——带权图最小生成树

问题描述

假设要在六个城市之间架设电视网线,5条边可以连接6个点,在这里我们应该选择哪5条边呢?不同的城市之间搭建线路的造价是有差异的。 所以,我们应该选择总造价成本最低的5条线路,符合这个要求的线路选择就是带权最小生成树
在这里插入图片描述

prin算法实现思路

  • 先随意选择一个顶点作为起始点,一般选择A作为起始点,现在我们设U集合为当前所找到最小生成树里面的顶点,TE集合为所找到的边,现在状态如下:U={A};TE={};
  • 现在查找一个顶点在U集合中,另一个顶点在V-U集合中的最小权值,如下图,在红线相交的线上找最小值。通过图中我们可以看到边A-D的权值最小为4,那么将D加入到U集合,(A,D)加入到TE,状态如下:U={A,D};TE={(A,D)};

在这里插入图片描述

  • 继续寻找,现在状态为U={A,D};TE={(A,D)};在与红线相交的边上查找最小值。通过图中我们可以看到边A-B的权值最小为6,那么将B加入到U集合,(A,B)加入到TE,状态如下:U={A,D,B};TE={(A,D),(A,B)};
    在这里插入图片描述
    在这里插入图片描述
  • 如此循环,直到找到所有顶点为止,也就是集合U里的顶点和原集合V一样的时候。

Java实现

封装顶点的类

package wgraph;

/**
 * 顶点的类
 */
public class Vertex {
    public char label;
    public boolean isInTree;

    public Vertex(char label){
        this.label = label;
        isInTree = false;
    }

}

封装边的类

package wgraph;

/**
 * 封装边的类
 */
public class Edge {
    public int startVert;
    public int endVert;
    public int price;//权重

    public Edge(int startVert, int endVert, int price){
        this.startVert = startVert;
        this.endVert = endVert;
        this.price = price;
    }
}

盛放边集合的优先级队列

package wgraph;

/**
 * 盛放边的优先级队列
 */
public class ProprotyQ {
    private Edge[] edges;//不是个普通数组,是有序的数组,从大到小排列
    private final int MAX_SIZE = 20;//上面数组的默认长度
    private int size;//上面数组中真实保存的边的条数

    public ProprotyQ(){
        edges = new Edge[MAX_SIZE];
        size = 0;
    }

    //向队列中插入新的边
    public void insert(Edge edge){
        int i;
        for (i=0;i<size;i++){
            if (edge.price>=edges[i].price){
                break;
            }
        }
        //循环结束后,i停留在应该插入的位置
        //数组中i之后的元素后移
        for (int j=size-1;j>=i;j--){
            edges[j+1] = edges[j];
        }
        //将新的边插入到i的位置
        edges[i] = edge;
        size++;
    }

    //删除并获取到队列中最小权的边
    public Edge removeMin(){
        return edges[--size];
    }

    //删除指定下标的边
    public void removeN(int n){
        for (int i=n;i<size;i++){
            edges[i] = edges[i+1];
        }
        size--;
    }

    //获取最小的边,不删除
    public Edge peekMin() {
        return edges[size-1];
    }

    //判断队列是不是空
    public boolean isEmpty(){
        return size ==0;
    }

    public int getSize(){
        return size;
    }

    //获取指定下标的边
    public Edge peekN(int n){
        return edges[n];
    }
}

实现带权图最小生成树的类

package wgraph;

/**
 * 实现带权图最小生成树的类
 */
public class WGraph {
    //图的基本属性
    private Vertex[] vertexList;//保存顶点的数组
    private int[][] adjMat;//邻接矩阵
    private int nVerts;//图中存在的节点数量
    private final int MAX_VERTS = 20;//初始化一个图中的顶点最大的个数
    private ProprotyQ proprotyQ;//存放最小生成树中的边的优先级数组
    private int nTree;//已经求解到的最小生成树的顶点的下标
    private int currentVert;//当前顶点的下标,下面循环会用
    private final int INF = 1000000;//一个很大的数,在带权重的图中表示不连通

    //构造方法
    public WGraph(){
        vertexList = new Vertex[MAX_VERTS];
        adjMat = new int[MAX_VERTS][MAX_VERTS];
        for (int i=0;i<MAX_VERTS;i++){
            for (int j=0;j<MAX_VERTS;j++){
                adjMat[i][j] = INF;
            }
        }
        nVerts = 0;//初始状态,图内没有节点
        proprotyQ = new ProprotyQ();
    }

    //向图中插入新的顶点
    public void insert(char label){
        vertexList[nVerts++] = new Vertex(label);
    }

    //更新边,设置顶点的连接关系
    public void addEdge(int start, int end, int price){
        //要更新邻接矩阵中的两个元素
        adjMat[start][end] = price;
        adjMat[end][start] = price;
    }

    //打印指定的顶点中的label
    public void displayVertex(int v){
        System.out.print(vertexList[v].label);
    }

    //实现求解带权值的最小生成树
    public void mstw(){
        //第一个步骤,任意选择一个顶点
        currentVert = 0;
        while (nTree<nVerts-1){
            vertexList[currentVert].isInTree = true;//相当于把节点放入U中
            nTree++;

            for (int i=0;i<nVerts;i++){
                if (i==currentVert) continue;//遍历到了自己,不做操作
                if (vertexList[i].isInTree) continue;//遍历到的点已经在U集合里,不做操作
                int price = adjMat[currentVert][i];//对应边的权值
                if (price == INF) continue;//不邻接的顶点,不作操作
                //剩下的情况就是找到了符合要求的邻接顶点
                proprotyQ.insert(new Edge(currentVert,i,price));//把边插入优先级数组
            }

            if (proprotyQ.getSize()==0){
                System.out.println("当前顶点没有邻接顶点");
                return;//结束方法
            }

            //把以放到U集合中的顶点为起点和终点的边从优先级队列中删除
            for (int i=0;i<proprotyQ.getSize();i++){
                if (vertexList[proprotyQ.peekN(i).startVert].isInTree &&
                        vertexList[proprotyQ.peekN(i).endVert].isInTree){
                    //起点和重点都在U集合中
                    proprotyQ.removeN(i);
                }
            }

            Edge minEdge = proprotyQ.removeMin();//拿到权值最小的点
            int startV = minEdge.startVert;
            currentVert = minEdge.endVert;

            //输出找到的边,相当于把目标边放到TE集合中
            System.out.print(vertexList[startV].label);
            System.out.print(vertexList[currentVert].label);
            System.out.print("  ");
        }

        //把所有顶点的isInTree还原
        for (int i=0;i<nVerts;i++){
            vertexList[i].isInTree = false;
        }
    }
}

测试带权图最小生成树的类

package wgraph;

public class GraphMain {
    public static void main(String[] args) {
        WGraph theGraph = new WGraph();

        theGraph.insert('A'); //0
        theGraph.insert('B');//1
        theGraph.insert('C');//2
        theGraph.insert('D');//3
        theGraph.insert('E');//4
        theGraph.insert('F');//5

        theGraph.addEdge(0, 1, 6); // AB 6
        theGraph.addEdge(0, 3, 4); // AD 4
        theGraph.addEdge(1, 2, 10); // BC 10
        theGraph.addEdge(1, 3, 7); // BD 7
        theGraph.addEdge(1, 4, 7); // BE 7
        theGraph.addEdge(2, 3, 8); // CD 8
        theGraph.addEdge(2, 4, 5); // CE 5
        theGraph.addEdge(2, 5, 6); // CF 6
        theGraph.addEdge(3, 4, 12); // DE 12
        theGraph.addEdge(4, 5, 7); // EF 7

        theGraph.mstw();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值