学习数据结构笔记(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小智RE0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值