31.普里姆算法

最小生成树

一个有 n 个结点的带权无向图,在满足所有顶点都连接的前提下使得所有的边的权总和最小,即为最小生成树(Minimum Spanning Tree MST)。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

  1. N个顶点,一定有N-1条边
  2. 包含所有顶点
  3. 所有顶点都可以直接或间接连接到另外的顶点

普里姆算法

普里姆算法在找最小生成树时,将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 A 类),剩下的是另一类(假设为 B 类)。

对于给定的连通网,起始状态全部顶点都归为 B 类。在找最小生成树时,选定任意一个顶点作为起始点,并将之从 B 类移至 A 类;然后找出 B 类中到 A 类中的顶点之间权值最小的顶点,将之从 B 类移至 A 类,如此重复,直到 B 类中没有顶点为止。所走过的顶点和边就是该连通图的最小生成树。

  1. 创建一个VisitedList保存所有已经被访问过的顶点
  2. 从任意顶点A开始构建最小生成树,标记顶点V已经被访问,加入被访问过顶点集合VisitedList
  3. 遍历被访问顶点集合 找到在不构成回路的前提下权值最小的边,将新增的顶点加入VisitedList
  4. 循环直到为顶点-1条边构建完成
图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GKbRWwLJ-1597226001274)(C:\Users\denglw\AppData\Roaming\Typora\typora-user-images\image-20200807113921613.png)]

1.从顶点开始处理 ======> <A,G> 2
A-C [7] A-G[2] A-B[5] =>

  1. <A,G> 开始 , 将A 和 G 顶点和他们相邻的还没有访问的顶点进行处理 =》<A,G,B>
    A-C[7] A-B[5] G-B[3] G-E[4] G-F[6]

  2. <A,G,B> 开始,将A,G,B 顶点 和他们相邻的还没有访问的顶点进行处理=><A,G,B,E>
    A-C[7] G-E[4] G-F[6] B-D[9]

    4.{A,G,B,E}->F//第4次大循环 , 对应 边<E,F> 权值:5
    5.{A,G,B,E,F}->D//第5次大循环 , 对应 边<F,D> 权值:4

  3. {A,G,B,E,F,D}->C//第6次大循环 , 对应 边<A,C> 权值:7 ===> <A,G,B,E,F,D,C>

代码示例

public class PrimAlgorithmDemo {

    public static void main(String[] args) {
        //测试看看图是否创建ok
        char[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int vertex = data.length;
        //邻接矩阵的关系使用二维数组表示,10000这个大数,表示两个点不联通
        int[][] weight = new int[][]{
                {10000, 5, 7, 10000, 10000, 10000, 2},
                {5, 10000, 10000, 9, 10000, 10000, 3},
                {7, 10000, 10000, 10000, 8, 10000, 10000},
                {10000, 9, 10000, 10000, 10000, 4, 10000},
                {10000, 10000, 8, 10000, 10000, 5, 4},
                {10000, 10000, 10000, 4, 5, 10000, 6},
                {2, 3, 10000, 10000, 4, 6, 10000},};
        MGraph graph = new MGraph(vertex);
        MiniTree miniTree = new MiniTree();
        miniTree.createGraph(graph, vertex, data, weight);
        miniTree.prim(graph, 0);
    }

}

class MiniTree {
    //创建图的邻接矩阵

    /**
     * @param graph  图对象
     * @param vertex 图对应的顶点个数
     * @param data   图的各个顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph graph, int vertex, char data[], int[][] weight) {
        int i, j;
        for (i = 0; i < vertex; i++) {//顶点
            graph.data[i] = data[i];
            for (j = 0; j < vertex; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

    //显示图的邻接矩阵
    public void showGraph(MGraph graph) {
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 使用普里姆算法生成最小生成树
     *
     * @param graph 图
     * @param start 开始顶点
     */
    public void prim(MGraph graph, int start) {
        // 创建一个set用于保存 所有已经被访问过的顶点
        Set<Integer> visited = new HashSet<>();
        visited.add(start);
        // 一直所有顶点都被访问才结束
        int v1 = 10000; // 变量记录当前顶点
        int v2 = 10000; // 记录下一个连接的顶点
        while (visited.size() < graph.vertex) {
            int minWeight = 10000;
            // 遍历循环已经 访问过的顶点
            for (Integer v : visited) {
                // 获取已经访问顶点中所有 非回路的边中 权值最小的一个
                for (int i = 0; i < graph.vertex; i++) {
                    // 如果当前顶点还没有被访问过 并且 找到权值最小的一个
                    // 这里v是已经联通了点 i是还未联通的点 graph.weight[v][i] < minWeight本质上是在找一条权值最小的一条边
                    if (!visited.contains(i) && graph.weight[v][i] < minWeight) {
                        // 记录当前最小权值边的顶点和权值
                        v1 = v;
                        v2 = i;
                        minWeight = graph.weight[v][i];
                    }
                }
            }
            // 上面双层for循环完之后就已经找到了 v2下一个如要加入的顶点 和最小权限 minWeight
            // 将v2标记为已访问
            visited.add(v2);
            // 打印信息
            System.out.println(graph.data[v1] + " => " + graph.data[v2] + " weight=" + minWeight);
        }
    }

}


class MGraph {
    int vertex; //表示图的节点个数
    char[] data;//存放结点数据
    int[][] weight; //存放边,就是我们的邻接矩阵

    public MGraph(int vertex) {
        this.vertex = vertex;
        data = new char[vertex];
        weight = new int[vertex][vertex];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值