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

news2024/11/17 12:37:02

目录

  • 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) 这篇文章中的最小生成树章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈滑动窗口

滑动窗口是什么&#xff1f; 滑动窗口其实是一个想象出来的数据结构。有左边界L和有边界R。 举例来说&#xff1a;数组 arr {3,1,5,7,6,5,8}; 其窗口就是我们规定的一个运动轨迹。 最开始时&#xff0c;边界LR都在数组的最左侧&#xff0c;此时没有包住任何数。 此时规定&…

测试老鸟总结,Web/APP与接口测试测试流程总结,避背黑锅...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、web测试流程 …

数据结构——树状数组

文章目录 前言问题引入问题分析树状数组lowbit树状数组特性初始化一个树状数组更新操作前缀和计算区间查询 总结 前言 原题的连接 最近刷leetcode的每日一题的时候&#xff0c;遇到了一个区间查询的问题&#xff0c;使用了一种特殊的数据结构树状数组&#xff0c;学习完之后我…

智慧工地APP全套源码,智慧工地云平台

智慧工地平台 &#xff0c;智慧工地源码&#xff0c;智慧工地APP全套源码 智慧工地以施工现场风险预知和联动预控为目标&#xff0c;将智能AI、传感技术、人像识别、监控、虚拟现实、物联网、5G、大数据、互联网等新一代科技信息技术植入到建筑、机械、人员穿戴设施、场地进出关…

SOME/IP 协议介绍(五)指南

指南&#xff08;信息性&#xff09; 选择传输协议 SOME/IP直接支持互联网上使用最广泛的两种传输协议&#xff1a;用户数据报协议&#xff08;UDP&#xff09;和传输控制协议&#xff08;TCP&#xff09;。UDP是一种非常简洁的传输协议&#xff0c;仅支持最重要的功能&#…

03. Python中的语句

1、前言 在《Python基础数据类型》一文中&#xff0c;我们了解了Python中的基础数据类型&#xff0c;今天我们继续了解下Python中的语句和函数。 2、语句 在Python中常用的语句可以大致分为两类&#xff1a;条件语句、循环语句。 2.1、条件语句 条件语句就是我们编码时常见…

C语言ASCII码排序(1086: ASCII码排序(多实例测试))

题目描述 输入三个字符后&#xff0c;按各字符的ASCII码从小到大的顺序输出这三个字符。 输入&#xff1a;输入数据有多组&#xff0c;每组占一行&#xff0c;由三个字符组成&#xff0c;之间无空格。chu 输出&#xff1a;对于每组输入数据&#xff0c;输出一行&#xff0c;字符…

Codeforces Round 909 (Div. 3)(A~G)(启发式合并 , DSU ON TREE)

1899A - Game with Integers 题意&#xff1a;给定一个数 , 两个人玩游戏&#xff0c;每人能够执行 操作&#xff0c;若操作完是3的倍数则获胜&#xff0c;问先手的人能否获胜&#xff08;若无限循环则先手的人输&#xff09;。 思路&#xff1a;假如一个数模3余1或者2&#…

【具身智能评估2】具身视觉语言规划(EVLP)数据集基准汇总

参考论文&#xff1a;Core Challenges in Embodied Vision-Language Planning 论文作者&#xff1a;Jonathan Francis, Nariaki Kitamura, Felix Labelle, Xiaopeng Lu, Ingrid Navarro, Jean Oh 论文原文&#xff1a;https://arxiv.org/abs/2106.13948 论文出处&#xff1a;Jo…

Python PyQt 程序设置图标

源码运行时图标 第一步&#xff1a;阿里巴巴是两图标库下载喜欢的图标 iconfont-阿里巴巴矢量图标库 第二步&#xff1a;转化png为ico https://www.aconvert.com/cn/icon/png-to-ico/ 256x256为大图标 默认的32x32很小&#xff08;不建议用) 转化后右键点击文件链接&…

基于金鹰算法优化概率神经网络PNN的分类预测 - 附代码

基于金鹰算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于金鹰算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于金鹰优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

【Android】Android Framework系列--CarUsbHandler源码分析

Android Framework系列–CarUsbHandler源码分析 本文基于Android12源码。 CarUsbHandler是Android Car提供的服务之一&#xff0c;其用车载USB连接的场景。 车载USB有其特殊应用场景&#xff0c;比如AndroidAuto、CarLife等。而Android的做法是在其原有的USB服务上&#xff0…

十七、Linux的组管理

1、Linux组基本介绍 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在linux中每个文件所有者、所在组、其它组的概念 1.所有者 2.所在组 3.其他组 4.改变用户所在的组 2、文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自…

【图解算法】- 快乐数还能这么解?

一 - 前言 介绍&#xff1a;大家好啊&#xff0c;我是hitzaki辰。 社区&#xff1a;&#xff08;完全免费、欢迎加入&#xff09;日常打卡、学习交流、资源共享的知识星球。 自媒体&#xff1a;我会在b站/抖音更新视频讲解 或 一些纯技术外的分享&#xff0c;账号同名&#xff…

MongoDB随记

MongoDB 1、简单介绍2、基本术语3、shard分片概述背景架构路由功能chunk&#xff08;数据分片&#xff09;shard key&#xff08;分片键值&#xff09; 4、常用命令 1、简单介绍 MongoDB是一个分布式文件存储的数据库&#xff0c;介于关系数据库和非关系数据库之间&#xff0c…

第 372 场 LeetCode 周赛题解

A 使三个字符串相等 求三个串的最长公共前缀 class Solution { public:int findMinimumOperations(string s1, string s2, string s3) {int n1 s1.size(), n2 s2.size(), n3 s3.size();int i 0;for (; i < min({n1, n2, n3}); i)if (!(s1[i] s2[i] && s2[i] s…

【智能家居】5、主流程设计以及外设框架编写与测试

目录 一、主流程设计 1、工厂模式结构体定义 &#xff08;1&#xff09;指令工厂 inputCmd.h &#xff08;2&#xff09;外设工厂 controlDevices.h 二、外设框架编写 1、创建外设工厂对象bathroomLight 2、编写相关函数框架 3、将浴室灯相关操作插入外设工厂链表等待被调…

内容运营工具:标签体系

一.分类和标签的区别 ■标签是扁平的&#xff0c;分类是层级的。 ■标签是精确的&#xff0c;分类是粗糙的。 ■标签是多维的&#xff0c;分类是一维的。 二.标签的本质&#xff1a;元数据 事实上&#xff0c;在数据领域&#xff0c;有一个鼎鼎大名的词汇与标签极其雷同&…

再高级的打工人也只是打工人!

再高级的打工人也只是打工人&#xff01; OpenAI CEO 奥特曼被罢免的事情人尽皆知「虽然&#xff0c;今天又复职了。。」&#xff0c;我们能从中学到什么呢&#xff1f; CEO 也能被裁&#xff0c;这应该是最近几年被裁名单里面&#xff0c;职级最高的一个人了吧。你再也不用担…

吐血整理,金融银行测试的“火“到底在哪里?银行测试真正实施...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行里的软件测试…