学习数据结构笔记(20) --- [普利姆算法(PrimAlgorithm) 由村庄的修路问题找最短权值引入]

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习数据结构笔记(20) --- [普利姆算法(PrimAlgorithm) 由村庄的修路问题找最短权值引入]相关的知识,希望对你有一定的参考价值。

B站学习传送门–>尚硅谷Java数据结构与java算法(Java数据结构与算法)


情景引入

假设现有7个村庄准备修公路,怎么样选择,才能让修路的里程最短并且连通所有的村庄?

直接连通的话,可能权值就比较大了;
那么这道题目其实可以转换为求解构建最小生成树的问题;

最小生成树:
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

克鲁斯卡尔算法将在本篇之后进行学习,本次学习使用普利姆算法解决这个问题;

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。

步骤分析

由普利姆算法求解最小生成树时,最终需要对n个结点生成n-1条边即可;且为极小连通子图;

具体过程:
定义连通图G:(V,E), 最小生成树:T:(U,D),
从连通图G的第一个顶点出发,找到权值最小的路径,放入最小生成树中,将这个路径连接的结点标记为已访问,
然后这两个顶点分别出发,找路径最短的,…重复操作;直到将连通图中的顶点全部访问;

图解过程:
(1)比如,先找到第一个村庄A, 对路径AB5,AC7,AG2比较后发现AG最短,则将A村庄与G村庄连通;此时A和G标记为已访问结点
然后,这时的出发点就是A点和G点了
(2) 比较路径 AB5,AC7,GB2,GE4,GF6后,找到最短路径GB.将G村庄与B村庄连通; B村庄标记为已访问结点;
这时出发点就是A点,B点,G点;

(3)比较路径AC7,GE4,GF6,BD9后,找到最短路径GE,将G村庄与E村庄连通,且将E村庄标记为已访问结点;
这时出发点是A点,B点,G点,E点;
(4) 比较路径AC7,GF6,BD9,EC8,EF5后,找到最短路径EF,将F村庄与E村庄连通,且将F村庄标记为已访问结点;
这时出发点就是A点,B点,G点,E点,F点;
(5)比较路径AC7,BD9,EC8,FD4后,找到最短路径FD,将村庄D与F村庄连通,且将D村庄标记为已访问结点;
这时出发点就是A点,B点,G点,E点,F点,D点;
(6)比较路径AC7,EC8后,找到最短路径AC,将村庄C与A连通,C村庄标记为已访问结点;
修路结束;得到最短路径25

具体实现

/**
 * @author by CSDN@小智RE0
 * @date 2021-12-07
 * 普利姆算法
 */
public class PrimAlgorithm 
    public static void main(String[] args) 
        //构建村庄修路问题的图;
        char[] village = 'A', 'B', 'C', 'D', 'E', 'F', 'G';
        //村庄的个数;
        int size = village.length;
        //手动构建村庄之间的连接权值邻接矩阵;
        int[][] villagePrimitive
                = Integer.MAX_VALUE, 5, 7, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 2,
                5, Integer.MAX_VALUE, Integer.MAX_VALUE, 9, Integer.MAX_VALUE, Integer.MAX_VALUE, 3,
                7, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 8, Integer.MAX_VALUE, Integer.MAX_VALUE,
                Integer.MAX_VALUE, 9, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 4, Integer.MAX_VALUE,
                Integer.MAX_VALUE, Integer.MAX_VALUE, 8, Integer.MAX_VALUE, Integer.MAX_VALUE, 5, 4,
                Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 4, 5, Integer.MAX_VALUE, 6,
                2, 3, Integer.MAX_VALUE, Integer.MAX_VALUE, 4, 6, Integer.MAX_VALUE;
        //查看初始化的数据;
        Graph graph = new Graph(size);
        MST mst = new MST();
        mst.getMST(size, graph, village, villagePrimitive);
        mst.printGraph(graph);
        //进行最短路径计算;
        mst.primAlgorithm(graph, 0);
    


//基础的图;
class Graph 
    //顶点;
    public char[] node;
    //路径权值;
    public int[][] weight;
    //顶点个数;
    public int size;

    //初始化;size:节点个数;
    public Graph(int size) 
        this.node = new char[size];
        this.weight = new int[size][size];
        this.size = size;
    


//最小生成树;
class MST 
    /**
     * 生成最小树
     * @param size   图的节点个数
     * @param graph  构建的图
     * @param node   顶点
     * @param weight 权值
     */
    public void getMST(int size, Graph graph, char[] node, int[][] weight) 
        for (int i = 0; i < size; i++) 
            graph.node[i] = node[i];
            System.arraycopy(weight[i], 0, graph.weight[i], 0, size);
        
    

    /**
     * 简易的普利姆算法;在当前节点为出发点找到的最小权值边路径,且进行路径打印;
     * @param graph 当前问题的图
     * @param index 当前的节点索引
     */
    public void primAlgorithm(Graph graph, int index) 
        //定义一个访问数组;
        boolean[] isVisited = new boolean[graph.size];
        //当前点先给他标记已访问;
        isVisited[index] = true;
        //两个要连通的节点 start,end
        int start = -1;
        int end = -1;
        //最短的路径;
        int minPath = Integer.MAX_VALUE;
        //最终的路径和;
        int total = 0;
        //最终会走6次;
        for (int i = 1; i < graph.size; i++) 
            for (int j = 0; j < graph.size; j++) 
                for (int k = 0; k < graph.size; k++) 
                    //要找的是没有访问的;
                    if (isVisited[j] && !isVisited[k] && minPath > graph.weight[j][k]) 
                        minPath = graph.weight[j][k];
                        start = j;
                        end = k;
                    
                
            
            System.out.println("当前路径" + graph.node[start] + "-->" + graph.node[end] + "权值为->" + minPath);
            //最终路径和拼接;
            total += minPath;
            //最终把符合的节点置为已访问;
            isVisited[end] = true;
            //最短路径重置;
            minPath = Integer.MAX_VALUE;
        

        System.out.println("最终的方案,最短路径为-->"+total);
    


    /**
     * 打印图的邻接矩阵
     *
     * @param graph 图
     */
    public void printGraph(Graph graph) 
        for (int[] weight : graph.weight) 
            System.out.println(Arrays.toString(weight));
        
    

测试结果

[2147483647, 5, 7, 2147483647, 2147483647, 2147483647, 2]
[5, 2147483647, 2147483647, 9, 2147483647, 2147483647, 3]
[7, 2147483647, 2147483647, 2147483647, 8, 2147483647, 2147483647]
[2147483647, 9, 2147483647, 2147483647, 2147483647, 4, 2147483647]
[2147483647, 2147483647, 8, 2147483647, 2147483647, 5, 4]
[2147483647, 2147483647, 2147483647, 4, 5, 2147483647, 6]
[2, 3, 2147483647, 2147483647, 4, 6, 2147483647]
当前路径A-->G权值为->2
当前路径G-->B权值为->3
当前路径G-->E权值为->4
当前路径E-->F权值为->5
当前路径F-->D权值为->4
当前路径A-->C权值为->7
最终的方案,最短路径为-->25

以上是关于学习数据结构笔记(20) --- [普利姆算法(PrimAlgorithm) 由村庄的修路问题找最短权值引入]的主要内容,如果未能解决你的问题,请参考以下文章

(王道408考研数据结构)第六章图-第四节1:最小生成树之普利姆算法(思想代码演示答题规范)

普利姆算法

图算法---普利姆算法

9.普利姆算法求最小生成树(JavaScript版)

数据结构最小生成树克鲁晓夫法和普利姆算法分析总结

数据结构 普利姆与克鲁斯卡尔的最小生成树(Swift面向对象版)