【算法基础实验】图论-最小生成树-Prim的即时实现
理论知识
Prim算法是一种用于计算加权无向图的最小生成树(MST, Minimum Spanning Tree)的贪心算法。最小生成树是一个连通的无向图的子图,它包含所有的顶点且总权重最小。Prim算法从一个起始顶点开始,不断将权重最小的边加入生成树,直到包含图中的所有顶点为止。
关键思想
- 起点选择:从任意一个顶点开始,将其标记为已访问。
- 贪心选择:从已访问的顶点集合中,选择一条连接到未访问顶点的权重最小的边,将该边加入MST。
- 更新过程:重复选择和标记过程,直到所有顶点都包含在MST中。
时间复杂度
- 使用简单的实现(如使用无序数组)时,Prim算法的时间复杂度为O(V^2)。
- 使用优先队列(如最小堆)优化时,时间复杂度可以降低到O(E log V),其中E是边的数量,V是顶点的数量。
算法流程
要改进 LazyPrimMST,可以尝试从优先队列中删除失效的边,这样优先队列就只含有树顶点和非树顶点之间的横切边。关键在于,我们感兴趣的只是连接树顶点和非树顶点中权重最小的边。当我们将顶点 v 添加到树中时,对于每个非树顶点 w 产生的变化只可能使得 w 到最小生成树的距离更近了,如图所示。简而言之,我们不需要在优先队列中保存所有从 w 到树顶点的边——而只需要保存其中权重最小的那条,在将 v 添加到树中后检查是否需要更新这条权重最小的边(因为v-w 的权重可能更小)。我们只需遍历 v 的邻接链表就可以完成这个任务。换句话说,我们只会在优先队列中保存每个非树顶点 w 的一条边:将它与树中的顶点连接起来的权重最小的那条边。将 w 和树的顶点连接起来的其他权重较大的边迟早都会失效,所以没必要在优先队列中保存它们。
PrimMST 类使用了索引优先队列实现的 Prim 算法。它将 LazyPrimMST 中的 marked[] 和 mst[] 替换为两个顶点索引的数组edgeTo[] 和 distTo[],它们具有如下性质。
- 如果顶点 v 不在树中但至少含有一条边和树相连,那么 edgeTo[v] 是将 v 和树连接的最短边,distTo[v] 为这条边的权重。
- 所有这类顶点 v 都保存在一条索引优先队列中,索引 v 关联的值是 edgeTo[v]的边的权重。
这些性质的关键在于优先队列中的最小键即是权重最小的横切边的权重,而和它相关联的顶点 v 就是下一个将被添加到树中的顶点。marked[] 数组已经没有必要了,因为判断条件 !marked[w] 等价于 distTo[w] 是无穷的(且 edgeTo[w] 为 null)。要维护这些数据结构,PrimMST 会从优先队列中取出一个顶点 v 并检查它的邻接链表中的每条边 v-w。如果 w 已经被标记过,那么这条边就已经失效了;如果 w 不在优先队列中或者 v-w 的权重小于目前已知的最小值 edgeTo[w],代码会更新数组,将 v-w 作为将 w 和树连接的最佳选择。

以下流程是基于本实验数据描绘的处理轨迹图:
解释流程图有助于对代码的理解,理解核心原理
关注数据的组织方式
在节点中:
- 白色圆形是树节点,灰色圆形是非树节点,
在边中:
-
红线代表当前加入优先队列的边
-
粗红线代表队列中权重最小的边
-
粗黑线代表已经纳入MST中的边
-
灰色线代表可以被其他线平替的线
💡 也就是说当我们遍历到一个非树节点时,需要判断当前遍历到的边和该非树节点已经被树遍历到的边相比,哪个最小,不是最小的都会被标记为灰色。如何记录树到非树节点的距离呢?是使用ditsTo[非树节点]这个数组来完成的
在Index列中:
- 红色数字代表节点已经被遍历
- 黑色数字代表该节点已经被纳入MST中了,marked置为True
- 灰色数字表示节点尚未被遍历
💡 遍历动作一定是由一个新晋的树节点发起的
当树节点遍历到了另一个树节点则直接跳过,根据切分定理,我们要遍历的是非树节点
在edgeTo列中:
- 黑色数字代表树节点
- 灰色数字代表非树节点
- 当边的两个节点都加入MST后,连接符号“-”由红色变为黑色
💡 新加入的边一定包含树节点,另外一端肯定要指向某个非树节点,非树节点是灰色的数字,这里的灰色数字一定是跟index值保持一致的,所以我们可以根据非树节点的编号作为索引来管理PQ队列,我们的处理核心是与树直连的非树节点
在distTo列中:
- 红色代表尚未确定边是否可以纳入MST
- 黑色代表确定该边已经加入了MST,并且已经从PQ中删除
- 红色箭头代表当前PQ中值最小的边

实验数据
8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93
代码实现
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;public class myPrimMST {private myEdge[] edgeTo;private double[] distTo;private boolean[] marked;private myIndexMinPQ<Double> pq;private double totalWeight;public myPrimMST(myEdgeWeightedGraph G){edgeTo = new myEdge[G.V()];distTo = new double[G.V()];marked = new boolean[G.V()];pq = new myIndexMinPQ<>(G.V());for(int v=0;v<G.V();v++)distTo[v] = Double.POSITIVE_INFINITY;distTo[0] = 0.0;pq.insert(0,0.0);while(!pq.isEmpty())visit(G,pq.delMin());}private void visit(myEdgeWeightedGraph G, int v){marked[v] = true;for(myEdge e: G.adj(v)){int w = e.other(v);if(marked[w]) continue;if(e.weight()<distTo[w]){edgeTo[w] = e;distTo[w] = e.weight();if(pq.contains(w)) pq.change(w,distTo[w]);else pq.insert(w,distTo[w]);}}}public Iterable<myEdge> edges(){myBag<myEdge> mst = new myBag<myEdge>();for(int v=1;v< edgeTo.length;v++)mst.add(edgeTo[v]);return mst;}public double weight(){totalWeight = 0.00;for(myEdge e:edges()){totalWeight +=e.weight();StdOut.println(totalWeight);}return totalWeight;}public static void main(String[] args){In in = new In(args[0]);myEdgeWeightedGraph G = new myEdgeWeightedGraph(in);myPrimMST mst = new myPrimMST(G);for(myEdge e:mst.edges())StdOut.println(e);StdOut.printf("%.5f\n", mst.weight());}
}
代码详解
类定义和成员变量
java复制代码
public class myPrimMST {private myEdge[] edgeTo; // 记录最小生成树的边private double[] distTo; // 记录从树到该顶点的最小权重边private boolean[] marked; // 记录顶点是否在树中private myIndexMinPQ<Double> pq; // 索引优先队列,帮助找到最小权重的边private double totalWeight; // 最小生成树的总权重
edgeTo:存储每个顶点的连接边,形成MST。distTo:存储到每个顶点的最小边权重。marked:标记哪些顶点已经被包括在MST中。pq:索引优先队列,用于动态查找最小边。
构造函数
java复制代码
public myPrimMST(myEdgeWeightedGraph G) {edgeTo = new myEdge[G.V()];distTo = new double[G.V()];marked = new boolean[G.V()];pq = new myIndexMinPQ<>(G.V());for(int v=0; v<G.V(); v++)distTo[v] = Double.POSITIVE_INFINITY;distTo[0] = 0.0;pq.insert(0, 0.0);while(!pq.isEmpty())visit(G, pq.delMin());
}
- 初始化:
edgeTo、distTo、marked和pq。 - 设置初始顶点的距离为
0并将其插入优先队列。 while循环:每次从优先队列中删除权重最小的顶点,并调用visit方法处理该顶点。
visit方法
java复制代码
private void visit(myEdgeWeightedGraph G, int v) {marked[v] = true;for(myEdge e: G.adj(v)) {int w = e.other(v);if(marked[w]) continue;if(e.weight() < distTo[w]) {edgeTo[w] = e;distTo[w] = e.weight();if(pq.contains(w)) pq.change(w, distTo[w]);else pq.insert(w, distTo[w]);}}
}
visit方法标记顶点v为已访问。- 遍历所有与
v连接的边,并检查另一端顶点w是否已在MST中。 - 如果找到更小的连接权重边,则更新
edgeTo和distTo,并在优先队列中更新或插入w。
edges方法和weight方法
java复制代码
public Iterable<myEdge> edges() {myBag<myEdge> mst = new myBag<>();for(int v=1; v<edgeTo.length; v++)mst.add(edgeTo[v]);return mst;
}public double weight() {totalWeight = 0.00;for(myEdge e : edges())totalWeight += e.weight();return totalWeight;
}
edges()方法返回MST中的所有边。weight()方法计算并返回MST的总权重。
main方法
java复制代码
public static void main(String[] args) {In in = new In(args[0]);myEdgeWeightedGraph G = new myEdgeWeightedGraph(in);myPrimMST mst = new myPrimMST(G);for(myEdge e : mst.edges())StdOut.println(e);StdOut.printf("%.5f\n", mst.weight());
}
- 从文件中读取图数据并构建
myEdgeWeightedGraph对象G。 - 使用
myPrimMST类计算最小生成树mst。 - 打印最小生成树的所有边和总权重。
总结
Prim算法通过逐步构建最小生成树,并利用优先队列来高效地选择最小权重的边。在这段代码中,myPrimMST 类实现了Prim算法,通过维护一个最小优先队列来管理尚未包括在MST中的顶点,从而动态调整生成树并计算其总权重。
实验步骤
C:\Users\xyz\IdeaProjects\algrithoms\src>javac myPrimMST.java
C:\Users\xyz\IdeaProjects\algrithoms\src>java myPrimMST data\tinyEWD.txt
2-7 0.34
6-2 0.40
7-5 0.28
5-4 0.35
1-3 0.29
0-2 0.26
5-1 0.32
2.24000
相关文章:
【算法基础实验】图论-最小生成树-Prim的即时实现
理论知识 Prim算法是一种用于计算加权无向图的最小生成树(MST, Minimum Spanning Tree)的贪心算法。最小生成树是一个连通的无向图的子图,它包含所有的顶点且总权重最小。Prim算法从一个起始顶点开始,不断将权重最小的边加入生成…...
LLama 3 跨各种 GPU 类型的基准测试
2024 年 4 月 18 日,AI 社区对 Llama 3 70B 的发布表示欢迎,这是一款最先进的大型语言模型 (LLM)。该型号是 Llama 系列的下一代产品,支持广泛的用例。该模型 istelf 在广泛的行业平台上表现良好,并提供了新…...
FreeRTOS 快速入门(五)之信号量
目录 一、信号量的特性1、信号量跟队列的对比2、两种信号量的对比 二、信号量1、二值信号量1.1 二值信号量用于同步1.2 二值信号量用于互斥 2、计数信号量 三、信号量函数1、创建2、删除3、give/take 一、信号量的特性 信号量(Semaphore)是一种实现任务…...
centos 服务器之间实现免密登录
为了在CentOS服务器之间实现免密登录,你需要使用SSH的公钥认证机制 比如两台centos系统的服务器A 和服务器B 首先我们实现从A服务器可以免密登录到服务器B上 首先生成公钥和秘钥: ssh-keygen -t rsa 生成了公钥和秘钥之后: ssh-copy-id r…...
RabbitMq实现延迟队列功能
1、rabbitmq服务端打开延迟插件 (超过 4294967295毫秒 ≈ 1193 小时 ≈ 49.7 天 这个时间会立即触发) 注意:只有RabbitMQ 3.6.x以上才支持 在下载好之后,解压得到.ez结尾的插件包,将其复制到RabbitMQ安装目录下的plug…...
redis内存淘汰策略
1. redis内存淘汰策略 日常常用:allkeys-lru:在键空间中移除最近最少使用的key。1.1 为什么需要使用redis内存淘汰策略? 因为我们服务器中的内存是有限的,不会无限多,所以需要对一些不常用的key进行内存清理.1.2 redis内存淘汰策略有哪些? redis默认…...
实时洞察应用健康:使用Spring Boot集成Prometheus和Grafana
1. 添加Prometheus和Actuator依赖 在pom.xml中添加Spring Boot Actuator和Micrometer Prometheus依赖: <dependencies> <!--监控功能Actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring…...
生信圆桌x生信豆芽菜:生物信息学新手的学习与成长平台
生信豆芽菜是一个专门为生物信息学初学者创建的学习与交流平台,致力于帮助新手们快速入门并掌握生信分析的基础知识与技能。随着生物信息学在科研中的重要性日益提升,越来越多的学生和研究人员开始接触这一领域。生信豆芽菜正是为了满足这些新手的需求&a…...
创客匠人标杆对话(上):她如何通过“特长+赛道”实现财富升级
老蒋创客圈第64期对话标杆直播连麦,本期我们邀请到【iAMU蒙特梭利翻转星球】平台创始人申晓慧老师。 为我们揭秘“如何挖掘人生首个百万,实现财富升级?”,深度分享如何提炼用户痛点,高效引流新用户?如何通…...
最少钱学习并构建大模型ollama-llama3 8B
学习大模型时可能面临一些困难,这些困难可能包括: 计算资源限制:训练大模型通常需要大量的计算资源,包括CPU、GPU等。如果设备资源有限,可能会导致训练时间长、效率低下或无法完成训练。 内存限制:大模型通…...
AVI视频损坏了怎么修复?轻松几步解决你的困扰
在数字化时代,视频已成为我们记录生活、分享经验和传递信息的重要方式。AVI作为一种常见的视频格式,因其无损质量的特点而受到广泛欢迎。然而,有时候我们可能会遇到AVI视频文件损坏的情况,导致无法正常播放。别担心,本…...
【C++】map、set基本用法
欢迎来到我的Blog,点击关注哦💕 前言: C的STL已经学习很大一部分了,接下来介绍的是map set是c的是两种关联容器。 简单介绍 map set: 两者都使用红黑树作为底层数据结构来存储元素。map是一种键值对容器,其中每个键…...
模型 闭环原理
系列文章 分享 模型,了解更多👉 模型_思维模型目录。反馈驱动,持续循环,缺陷亦被放大。 1 闭环原理的应用 1.1 闭环原理解读 AI自我训练,从人工智能变成人工智障 这里主要使用闭环原理来解释 AI 自我训练导致的问题。…...
3007. 价值和小于等于 K 的最大数字(24.8.21)
前言 感谢皇家笨阿宝的指导 题目 给你一个整数 k 和一个整数 x 。整数 num 的价值是它的二进制表示中在 x,2x,3x 等位置处设置位的数目(从最低有效位开始)。下面的表格包含了如何计算价值的例子。 XnumBinary RepresentationPri…...
微服务 - 分布式锁的实现与处理策略
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有疑问和建议,请私信或评论留言! 分布式锁的实现与处理…...
Catf1ag CTF Web(九)
前言 Catf1agCTF 是一个面向所有CTF(Capture The Flag)爱好者的综合训练平台,尤其适合新手学习和提升技能 。该平台由catf1ag团队打造,拥有超过200个原创题目,题目设计注重知识点的掌握,旨在帮助新手掌握C…...
QT QFileDialog 类
QFileDialog 类 QFileDialog 类 QFileDialog 是 Qt 库中的一个类,用于提供文件选择对话框, 允许用户选择文件或目录。QFileDialog 提供了多种静态方法和实例方法, 用于创建和配置文件对话框,并获取用户选择的文件或目录。 QObje…...
了解 K-Means 聚类的工作原理(详细指南)
一、说明 K-means 的目标是将一组观测值划分为 k 个聚类,每个观测值分配给均值(聚类中心或质心)最接近的聚类,从而充当该聚类的代表。 在本文中,我们将全面介绍 k 均值聚类(最常用的聚类方法之一࿰…...
预警先行,弯道哨兵让行车更安全
预警先行,弯道哨兵让行车更安全”这句话深刻体现了现代交通安全理念中预防为主、科技赋能的重要性。在道路交通中,尤其是复杂多变的弯道区域,交通事故的发生率往往较高,因此,采取有效的预警措施和引入先进的交通辅助设…...
预约咨询小程序搭建开发,uniapp前端,PHP语言开发
目录 前言: 一、预约小程序搭建功能介绍 二、示例代码片段 前言: 预约咨询小程序适合需付费咨询和交流的场景:比如讲师,摄影,婚庆,美发,律师,心理等等支持商家入驻支持视频、图文、线下、电话等方式在线支付咨询。 一、预约小程…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
