【算法】最小生成树——普利姆 (Prim) 算法

目录

  • 1.概述
  • 2.代码实现
    • 2.1.邻接矩阵存储图
    • 2.2.邻接表存储图
    • 2.3.测试
  • 3.应用

1.概述

(1)在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得联通所有结点w(T) 最小,则此 T 为 G 的最小生成树 (minimal spanning tree)

(2)普利姆 (Prim) 算法是一种用于解决最小生成树问题的贪心算法,其主要思路如下:

  • ① 选择任意一个顶点作为起始点,将其加入最小生成树中。
  • ② 从已选择的顶点集合中选取一个顶点,该顶点与未选择的顶点构成的边权重最小,并且该边的另一端顶点未被选择,将该顶点和边加入最小生成树中。
  • ③ 重复步骤 ②,直到最小生成树包含了图中的所有顶点。

(3)例如,对带权连通无向图 G 使用普利姆 (Prim) 算法构造最小生成树的过程如下:

请添加图片描述

另外一种生成最小生成树的克鲁斯卡尔 (Kruskal) 算法可参考【算法】最小生成树——克鲁斯卡尔 (Kruskal) 算法这篇文章。

2.代码实现

2.1.邻接矩阵存储图

class Solution {

    // INF 表示两点之间没有连接,即无穷大
    int INF = Integer.MAX_VALUE;

    /*
         graph: 用于表示图的邻接矩阵
         返回值: 路径矩阵
    */
    public int prim(int[][] graph) {
        //图中的顶点数
        int V = graph.length;
        // weight[i] 表示从 i 点到已访问集合的最小边权值
        int[] weight = new int[V];
        Arrays.fill(weight, INF);
        //标记节点是否在最小生成树中
        boolean[] mstSet = new boolean[V];
        // parent[i] 表示从 i 点到最小生成树的一条边
        int[] parent = new int[V];
        //从顶点 0 开始生成最小树
        weight[0] = 0;
        //根节点没有父节点
        parent[0] = -1;
        //访问 V - 1 个节点
        for (int i = 0; i < V - 1; i++) {
            //从未访问的节点中选择 weight 最小的节点 u
            int u = minKey(weight, mstSet);
            //将节点 u 标记为已访问
            mstSet[u] = true;
            //访问与 u 相邻的节点 v
            for (int v = 0; v < V; v++) {
                //如果 v 未被访问过、u - v 之间有边、并且 u - v 之间的距离比原本的距离小
                if (!mstSet[v] && graph[u][v] != 0 && graph[u][v] != INF && graph[u][v] < weight[v]) {
                    //将 u - v 之间的边加入最小生成树
                    parent[v] = u;
                    //标记从 v 到已访问集合的最小边权值
                    weight[v] = graph[u][v];
                }
            }
        }
        //计算最小生成树的权值并返回
        int sum = 0;
        for (int i = 1; i < V; i++) {
            sum += weight[i];
        }
        //输出最小生成树的路径
        System.out.println("最小生成树的路径以及对应的权重依次为: ");
        for (int i = 1; i < V; i++) {
            System.out.println("(" + parent[i] + "-" + i + ") " + weight[i]);
        }
        return sum;
    }

    public int minKey(int[] weight, boolean[] mstSet) {
        //初始化 weight 的最小值和对应的节点
        int min = INF;
        int minIndex = -1;
        for (int v = 0; v < weight.length; v++) {
            //如果 v 节点未被访问,并且 v 节点到已访问集合的边的权值更小
            if (!mstSet[v] && weight[v] < min) {
                //更新最小值
                min = weight[v];
                //更新 weight 的最小值对应的节点
                minIndex = v;
            }
        }
        return minIndex;
    }
}

2.2.邻接表存储图

class Solution {

    // INF 表示两点之间没有连接,即无穷大
    int INF = Integer.MAX_VALUE;

    /*
         graph: 用于表示图的邻接表
         返回值: 最小生成树的权重
    */
    public int prim(List<int[]>[] graph) {
        //图中的顶点数
        int V = graph.length;
        // weight[i] 表示从 i 点到已访问集合的最小边权值
        int[] weight = new int[V];
        Arrays.fill(weight, INF);
        //标记节点是否在最小生成树中
        boolean[] mstSet = new boolean[V];
        // parent[i] 表示从 i 点到最小生成树的一条边
        int[] parent = new int[V];
        //从顶点 0 开始生成最小树
        weight[0] = 0;
        //根节点没有父节点
        parent[0] = -1;
        //访问 V - 1 个节点
        for (int i = 0; i < V - 1; i++) {
            //从未访问的节点中选择 weight 最小的节点 u
            int u = minKey(weight, mstSet);
            //将节点 u 标记为已访问
            mstSet[u] = true;
            //访问与 u 相邻的节点 v
            for (int[] node : graph[u]) {
                int v = node[0];
                int w = node[1];
                if (!mstSet[v] && w < weight[v]) {
                    parent[v] = u;
                    weight[v] = w;
                }
            }
        }
        //计算最小生成树的权值并返回
        int sum = 0;
        for (int i = 1; i < V; i++) {
            sum += weight[i];
        }
        //输出最小生成树的路径
        System.out.println("最小生成树的路径以及对应的权重依次为: ");
        for (int i = 1; i < V; i++) {
            System.out.println("(" + parent[i] + "-" + i + ") " + weight[i]);
        }
        return sum;
    }

    public int minKey(int[] weight, boolean[] mstSet) {
        //初始化 weight 的最小值和对应的节点
        int min = INF;
        int minIndex = -1;
        for (int v = 0; v < weight.length; v++) {
            //如果 v 节点未被访问,并且 v 节点到已访问集合的边的权值更小
            if (!mstSet[v] && weight[v] < min) {
                //更新最小值
                min = weight[v];
                //更新 weight 的最小值对应的节点
                minIndex = v;
            }
        }
        return minIndex;
    }
}

2.3.测试

(1)本测试中的加权无向图如下所示:

在这里插入图片描述

(2)邻接矩阵的测试代码如下:

class Test {
	public static void main(String[] args) {
        //图的顶点数
        int n = 7;
        int[][] graph = new int[n][n];
        //初始化邻接矩阵,初始化为 Integer.MAX_VALUE 表示不可达
        for (int i = 0; i < n; i++) {
            Arrays.fill(graph[i], Integer.MAX_VALUE);
            graph[i][i] = 0;
        }
        //添加图的边
        graph[0][1] = 9;
        graph[0][5] = 1;
        graph[1][0] = 9;
        graph[1][2] = 4;
        graph[1][6] = 3;
        graph[2][1] = 4;
        graph[2][3] = 2;
        graph[3][2] = 2;
        graph[3][4] = 6;
        graph[3][6] = 5;
        graph[4][3] = 6;
        graph[4][5] = 8;
        graph[4][6] = 7;
        graph[5][0] = 1;
        graph[5][4] = 8;
        graph[6][1] = 3;
        graph[6][3] = 5;
        graph[6][4] = 7;

        Solution solution = new Solution();
        int sum = solution.prim(graph);
        System.out.println("最小生成树的权重为: " + sum);
    }
}

输出结果如下:

最小生成树的路径以及对应的权重依次为: 
(2-1) 4
(3-2) 2
(4-3) 6
(5-4) 8
(0-5) 1
(1-6) 3
最小生成树的权重为: 24

(3)邻接表的测试代码如下:

class Test {
	public static void main(String[] args) {
        //图的顶点数
        int n = 7;
        List<int[]>[] graph = new ArrayList[n];
        //初始化邻接表
        for (int i = 0; i < n; i++) {
            graph[i] = new ArrayList<>();
        }
        //添加图的边
        graph[0].add(new int[]{1, 9});
        graph[0].add(new int[]{5, 1});
        graph[1].add(new int[]{0, 9});
        graph[1].add(new int[]{2, 4});
        graph[1].add(new int[]{6, 3});
        graph[2].add(new int[]{1, 4});
        graph[2].add(new int[]{3, 2});
        graph[3].add(new int[]{2, 2});
        graph[3].add(new int[]{4, 6});
        graph[3].add(new int[]{6, 5});
        graph[4].add(new int[]{3, 6});
        graph[4].add(new int[]{5, 8});
        graph[4].add(new int[]{6, 7});
        graph[5].add(new int[]{0, 1});
        graph[5].add(new int[]{4, 8});
        graph[6].add(new int[]{1, 3});
        graph[6].add(new int[]{3, 5});
        graph[6].add(new int[]{4, 7});

        Solution solution = new Solution();
        int sum = solution.prim(graph);
        System.out.println("最小生成树的权重为: " + sum);
    }
}

输出结果如下:

最小生成树的路径以及对应的权重依次为: 
(2-1) 4
(3-2) 2
(4-3) 6
(5-4) 8
(0-5) 1
(1-6) 3
最小生成树的权重为: 24

3.应用

(1)求图的最小生成树许多实际应用,例如城市之间的交通工程造价最优问题就是一个最小生成树问题。

(2)大家可以去 LeetCode 上找相关的最小生成树的题目来练习,或者也可以直接查看 LeetCode 算法刷题目录 (Java) 这篇文章中的最小生成树章节。如果大家发现文章中的错误之处,可在评论区中指出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/165898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为模拟器dhcp实验

实验需求&#xff0c;pc1 pc2 pc3 获取到地址且能ping通&#xff0c;pc1 pc2 为地址池模式&#xff0c;pc3为接口模式 上配置 #sysname AR1# dhcp enable # interface GigabitEthernet0/0/0ip address 10.0.47.254 255.255.255.0 dhcp select relaydhcp relay server-ip 10.0…

认识.NET Aspire:高效构建云原生应用的利器

简介 在几天前的.NET 8发布会上&#xff0c;来自微软的Glenn Condron和David Fowler为我们演示了.NET Aspire&#xff0c;在Visual Studio的帮助下&#xff0c;它展现出了惊人的开发效率。 短短的十分钟内&#xff0c;David现场演示了如何轻松创建了一个具有服务发现&#xf…

基于不确定性感知的脑肿瘤分割多维互学习

Uncertainty-Aware Multi-Dimensional Mutual Learning for Brain and Brain Tumor Segmentation 一基于不确定性感知的脑肿瘤分割多维互学习背景贡献实验方法Uncertainty-Aware Mutual Learning&#xff08;具有不确定性的相互学习&#xff09; Thinking 一基于不确定性感知的…

设计模式常见面试题

简单梳理下二十三种设计模式&#xff0c;在使用设计模式的时候&#xff0c;不仅要对其分类了然于胸&#xff0c;还要了解每个设计模式的应用场景、设计与实现&#xff0c;以及其优缺点。同时&#xff0c;还要能区分功能相近的设计模式&#xff0c;避免出现误用的情况。 什么是…

Git精讲

Git基本操作 创建Git本地仓库 git initgit clone 配置Git git config [--global] user.name "Your Name" git config [--global] user.email "emailexample.com"–global是一个可选项。如果使用了该选项&#xff0c;表示这台机器上所有的Git仓库都会使…

Network(三)动态路由与ACL配置

一 三层交换机 1 三层交换机概述 三层交换二层交换三层转发 2 虚拟接口概述 在三层交换机上配置的VLAN接口为虚拟接口&#xff0c;使用Vlanif&#xff08;VLAN虚拟接口&#xff09;实现VLAN间路由&#xff0c;VLAN接口的引入使得应用更加灵活 三层交换机VLAN间通信的转发…

Cross-View Transformers for Real-Time Map-View Semantic Segmentation 论文阅读

论文链接 Cross-View Transformers for Real-Time Map-View Semantic Segmentation 0. Abstract 提出了 Cross-View Transformers &#xff0c;一种基于注意力的高效模型&#xff0c;用于来自多个摄像机的地图视图语义分割使用相机感知的跨视图注意机制隐式学习从单个相机视…

第93步 深度学习图像分割:PSPNet建模

基于WIN10的64位系统演示 一、写在前面 本期&#xff0c;我们继续学习深度学习图像分割系列的另一个模型&#xff0c;PSPNet。 二、PSPNet简介 &#xff08;1&#xff09;金字塔池化模块 (Pyramid Pooling Module) PSPNet的核心是其金字塔池化模块&#xff0c;该模块能够捕…

4 redis的HyperLogLog入门原理

一、HyperLogLog&#xff08;字符串类型&#xff09; 需求&#xff1a;大型网站(不在大厂基本上用不到) 每个网页每天的 UV 数据(独立访客)&#xff0c;统计如何实现&#xff1f;(尽量少的占用存储空间) Redis 提供了 HyperLogLog 数据结构就是用来解决这种统计问题的。Hyper…

[ 云计算 | AWS 实践 ] Java 如何重命名 Amazon S3 中的文件和文件夹

本文收录于【#云计算入门与实践 - AWS】专栏中&#xff0c;收录 AWS 入门与实践相关博文。 本文同步于个人公众号&#xff1a;【云计算洞察】 更多关于云计算技术内容敬请关注&#xff1a;CSDN【#云计算入门与实践 - AWS】专栏。 本系列已更新博文&#xff1a; [ 云计算 | …

六、文件上传漏洞

下面内容部分&#xff1a;参考 一、文件上传漏洞解释 解释&#xff1a;文件上传漏洞一般指的就是用户能够绕过服务器的规则设置将自己的木马程序放置于服务器实现远程shell&#xff08;例如使用蚁剑远程连接&#xff09;&#xff0c;常见的木马有一句话木马(php) 无需启用sho…

各类语言真实性能比较列表

这篇文章是我所做或将要做的所有真实世界性能比较的索引。如果你对想要看到的其他真实世界案例有建议&#xff0c;请在评论中添加。 用例 1 — JWT 验证 & MySQL 查询 该用例包括&#xff1a; 从授权头部获取 JWT验证 JWT 并从声明中获取电子邮件使用电子邮件执行 MySQL…

〖大前端 - 基础入门三大核心之JS篇㊳〗- DOM访问元素节点

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

SQL练习02

1.买下所有产品的客户 SQL Create table If Not Exists Customer (customer_id int, product_key int); Create table Product (product_key int); Truncate table Customer; insert into Customer (customer_id, product_key) values (1, 5); insert into Customer (customer_…

鸿蒙:使用Stack、ContentTable、Flex等组件和布局实现一个显示界面

效果展示 一.概述 跟随官网继续HarmonyOS学习 本篇博文实现一个食物详情页的开发Demo 通过这个开发过程学习如何使用容器组件Stack、Flex和基本组件Image、Text&#xff0c;构建用户自定义组件&#xff0c;完成图文并茂的食物介绍 二.构建Stack布局 1.食物名称 创建Stack…

YOLOv5 学习记录

文章目录 整体概况数据增强与前处理自适应Anchor的计算Lettorbox 架构SiLU激活函数YOLOv5改进点SSPF 模块 正负样本匹配损失函数 整体概况 YOLOv5 是一个基于 Anchor 的单阶段目标检测&#xff0c;其主要分为以下 5 个阶段&#xff1a; 1、输入端&#xff1a;Mosaic 数据增强、…

【LeetCode刷题-树】--100.相同的树

100.相同的树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

【数据结构】C语言实现队列

目录 前言 1. 队列 1.1 队列的概念 1.2 队列的结构 2. 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 入队 2.4 出队 2.5 获取队头元素 2.6 获取队尾元素 2.7 判断空队列 2.8 队列的销毁 3. 队列完整源码 Queue.h Queue.c &#x1f388;个人主页&#xff1a…

获取每个部门中当前员工薪水最高的相关信息

个人网站 首发于公众号小肖学数据分析 描述 有一个员工表dept_emp简况如下: 有一个薪水表salaries简况如下: 获取每个部门中当前员工薪水最高的相关信息&#xff0c;给出dept_no, emp_no以及其对应的salary&#xff0c;按照部门编号dept_no升序排列&#xff0c;以上例子输出…

【NI-DAQmx入门】校准

1.设备定期校准的理由 随着时间的推移电子器件的特性会发生自然漂移&#xff0c;可能会导致测量结果的不准确性。防止出现良品和差品筛选出错的情况满足行业国际标准降低设备出现故障的风险使测量结果更具备参考性 2.查找NI设备的校准间隔。 定期校准会使DAQ设备的精度保持在…