【算法基础实验】图论-最小生成树-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语言开发
目录 前言: 一、预约小程序搭建功能介绍 二、示例代码片段 前言: 预约咨询小程序适合需付费咨询和交流的场景:比如讲师,摄影,婚庆,美发,律师,心理等等支持商家入驻支持视频、图文、线下、电话等方式在线支付咨询。 一、预约小程…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题
20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题 2025/6/9 20:54 缘起,为了跨网段推流,千辛万苦配置好了网络参数。 但是命令iptables -t filter -F tetherctrl_FORWARD可以在调试串口/DEBUG口正确执行。…...
