图的最小生成树算法: Prim算法和Kruskal算法(C++)
上一节我们学习了最短路径算法, 这一节来学习最小生成树.
最小生成树(Minimum Spanning Tree, MST)算法是图论中的一种重要算法, 主要用于在加权无向图中找到一棵生成树, 使得这棵树包含图中的所有顶点, 并且所有边的权重之和最小. 这样的树被称为最小生成树. 最小生成树广泛应用于网络设计, 电路布线等领域. 主要有两种算法 Prim 算法和 Kruskal 算法.
环境要求
本文所用样例在Windows 11
以及Ubuntu 24.04
上面编译通过.
- Windows: 使用[Visual Studio],
- Ubuntu: 使用 Clang 18.1.3. (Ubuntu 24.04 系统安装版本)
- GCC 无法编译直接本项目代码, 因为本文代码使用了 C++20 Module, 而 GCC 对此支持不完整.
关于 Module 的更多信息, 请参考我之前的博客: CMake 构建 C++20 Module 实例(使用 MSVC)
本项目工程目录: 图论代码
Prim 算法
Prim 算法是一种用于寻找加权无向图的最小生成树(Minimum Spanning Tree, MST)的经典贪心算法. 它由捷克数学家 Vojtěch Jarník 在 1930 年提出, 后来又被计算机科学家 Robert C. Prim 独立发现, 并因此得名. Prim 算法特别适用于稠密图, 即边的数量接近顶点数平方的情况.
算法步骤
Prim 算法的基本思想是从一个任意选择的起始顶点开始构建最小生成树, 逐步将距离当前生成树最近的顶点加入到生成树中, 直到所有顶点都被包含为止. 具体步骤如下:
-
初始化:
- 选择任意一个顶点作为起始点, 将其标记为已访问.
- 初始化一个优先队列(或最小堆), 用来存储尚未访问的顶点及其与当前生成树的最短距离. 初始时, 除了起始顶点外, 其他所有顶点的距离设为无穷大(表示还未连接).
-
迭代过程:
- 从优先队列中取出距离当前生成树最近的顶点 u u u, 并将其标记为已访问.
- 对于顶点 u u u 的所有邻接顶点 v v v, 如果 v v v 尚未被访问, 并且通过 u u u 到达 v v v 的距离比之前记录的距离更短, 则更新 v v v 的距离值, 并将( v v v, 距离)对插入或更新到优先队列中.
-
终止条件:
- 当优先队列为空, 或者所有顶点都已被访问时, 算法结束. 此时, 已经找到了最小生成树.
伪代码
// 输入: 一个加权无向图G = (V, E), 其中V是顶点集合, E是边集合
// 输出: 最小生成树MST的边集Prim(G, start_vertex):// 初始化MST = [] // 存储最小生成树的边priority_queue = new MinHeap() // 优先队列(最小堆), 存储(权重, 顶点)对visited = new Set() // 已访问顶点集合add start_vertex to visited// 将起始顶点的所有邻接边加入优先队列for each neighbor in G.adjacent(start_vertex):if neighbor not in visited:priority_queue.insert((neighbor, weight(start_vertex, neighbor), start_vertex))// 主循环while not priority_queue.isEmpty():// 取出优先队列中权重最小的边(u, v)(u, weight_uv, previous_u) = priority_queue.extractMin()if u in visited:continue // 如果顶点u已经被访问过, 则跳过// 将顶点u标记为已访问, 并将边(previous_u, u)加入MSTadd u to visitedadd (previous_u, u, weight_uv) to MST// 对于顶点u的所有邻接顶点vfor each (v, weight_u_v) in G.adjacent(u):if v not in visited:// 将(v, 权重)对插入或更新到优先队列中priority_queue.insertOrDecreaseKey((v, weight_u_v, u))return MST
样例
考虑下面这个图, 求它的最小生成树.
-
初始化: 假设我们从
G
开始访问, 此时标记G
为已访问, 并将与G
相邻的点加入到优先队列中. 设置其他点的距离为无穷大.
-
迭代过程:
-
从优先队列中取出
(G, C)
, 将C
标记为已访问, 将G-C
这条边加入到结果集中. 访问C
的邻接点[A, B, D]
, 更新他们的距离, 由于新的距离更小, 所以将[A, B, D]
加入优先队列中.
-
从优先队列中取出
(C, A)
. 将A
标记为已访问, 将C-A
这条边加入到结果集中. 访问A
的邻接点[B, C, D, H]
, 其中C
已经访问过, 跳过. 将其他的边加入优先队列中. -
从优先队列中取出
(A, B)
. 将B
标记为已访问, 将A-B
这条边加入到结果集中. 访问B
的邻接点[A, C, D, E]
,A
,C
均已访问, 跳过; 将其他的边加入优先队列中.
-
从优先队列中取出
(B, E)
. 将E
标记为已访问, 将B-E
这条边加入到结果集中. 访问E
的邻接点[B, D, F, H]
,B
以访问,跳过. 将其他的边加入优先队列中.
-
从优先队列中取出
(B, D)
. 将D
标记为已访问, 将B-D
这条边加入到结果集中. 访问D
的邻接点[A, B, C, E, F]
, 其中[A, B, C, E]
已访问, 跳过. 将D,F
加入优先队列.
-
跳过
(C, B)
,(A,D)
,(C,D)
-
从优先队列中取出
(D, F)
. 将F
标记为已访问, 将D-F
这条边加入到结果集中. 访问F
的邻接点[D, E]
, 均已访问过, 跳过.
-
从优先队列中取出
(G, H)
. 将H
标记为已访问, 将G-H
这条边加入到结果集中. 访问H
的邻接点[A, E, G]
, 均已访问, 跳过. -
其他边的顶点都已经访问过, 均被跳过, 算法结束. 得到的最小生成树如下:
实现细节
typedef std::pair<unsigned, int> EdgeWithWeight;
class Prim {public:
相关文章:

图的最小生成树算法: Prim算法和Kruskal算法(C++)
上一节我们学习了最短路径算法, 这一节来学习最小生成树. 最小生成树(Minimum Spanning Tree, MST)算法是图论中的一种重要算法, 主要用于在加权无向图中找到一棵生成树, 使得这棵树包含图中的所有顶点, 并且所有边的权重之和最小. 这样的树被称为最小生成树. 最小生成树广泛应…...

WPS的AI助手进化跟踪(灵犀+插件)
Ver V0.0 250216: 如何给WPS安装插件用以支持其他大模型LLM V0.1 250217: WPS的灵犀AI现在是DeepSeek R1(可能是全参数671B) 前言 WPS也有内置的AI,叫灵犀,之前应是自已的LLM模型,只能说是属于“能用,有好过无”,所…...

我用AI做数据分析之数据清洗
我用AI做数据分析之数据清洗 AI与数据分析的融合效果怎样? 这里描述自己在使用AI进行数据分析(数据清洗)过程中的几个小故事: 1. 变量名的翻译 有一个项目是某医生自己收集的数据,变量名使用的是中文,分…...
一周学会Flask3 Python Web开发-request请求对象与url传参
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili request请求对象封装了从客户端发来的请求报文信息,我们可以从中获取所有数据。 request对象包含的常用属性&…...
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析④】
ISO 14229-1:2023 UDS诊断【ECU复位0x11服务】_TestCase04 作者:车端域控测试工程师 更新日期:2025年02月17日 关键词:UDS诊断协议、ECU复位服务、0x11服务、ISO 14229-1:2023 TC11-004测试用例 用例ID测试场景验证要点参考条款预期结果TC…...

网络技术变迁:从IPv4走向IPv6
目录 前言 旧时代产物:IPv4 什么是IPv4? IPv4的工作方式 IPv4的缺点 为什么要从IPv4过渡到IPv6? 走向IPv6:新一代互联网协议 IPv6的技术特性 我们需要过渡技术 双栈(Dual Stack) 隧道技术&#…...
DeepSeek教unity------事件管理
1. 定义事件类型 定义一个枚举来表示不同类型的事件。组织和识别不同的事件。 2. 创建事件参数类 为了让事件携带数据,创建一个通用的事件参数类或者为每个事件类型创建特定的参数类。 3. 实现事件管理器 创建一个EventManager类,用于管理事件的注册…...

确保设备始终处于最佳运行状态,延长设备的使用寿命,保障系统的稳定运行的智慧地产开源了
智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。通过计算机视觉和…...

RedisTemplate存储含有特殊字符解决
ERROR信息: 案发时间: 2025-02-18 01:01 案发现场: UserServiceImpl.java 嫌疑人: stringRedisTemplate.opsForValue().set(SystemConstants.LOGIN_CODE_PREFIX phone, code, Duration.ofMinutes(3L)); // 3分钟过期作案动机: stringRedisTemplate继承了Redistemplate 使用的…...
28、深度学习-自学之路-NLP自然语言处理-做一个完形填空,让机器学习更多的内容程序展示
import sys,random,math from collections import Counter import numpy as npnp.random.seed(1) random.seed(1) f open(reviews.txt) raw_reviews f.readlines() f.close()tokens list(map(lambda x:(x.split(" ")),raw_reviews))#wordcnt Counter() 这行代码的…...

【NLP 22、语言模型 language model】
有时候我也想听听,我在你心里,是什么样子 —— 25.1.12 一、什么是语言模型 语言是灵活的,也是有规律的 了解一门语言的人可以判断一句话是否“合理” 通俗来讲,语言模型用来评价一句话(句子可以看作是字的组合)是否“合理”或…...
刚性平衡机建模
这两个公式是动平衡机中用于描述旋转部件振动行为的动力学方程。它们分别描述了旋转部件在平移振动和扭转振动中的运动规律,用于分析不平衡量对系统的影响。以下是详细解释: 1. 第一个公式:平移振动的动力学方程 M d 2 y d t 2 2 K y 0 m 1…...

【算法】双指针(上)
目录 双指针 左右指针(对撞指针) 快慢指针 移动零 双指针解题 复写零 暴力解题 双指针解题(快慢指针) 快乐数 双指针解题(快慢指针) 盛最多水的容器 暴力解题(会超时) 双指针解题(左右指针) 有效三角形的个数 暴力解题 双指针解题(左右指针) 双指针 常见的双指…...

【Linux Redis】关于用docker拉取Redis后,让虚拟机运行起来redis,并使得其可以连接到虚拟机外的navicat。
步骤一:拉取Redis镜像 docker pull redis 这个命令会下载最新版本的Redis镜像到你的本地Docker仓库中。你也可以指定一个具体的版本号,例如docker pull redis:6.2.6,来拉取特定版本的Redis镜像。 如果拉取遇到问题请参考【Linux AnolisOS】关…...
用deepseek学大模型04-模型可视化与数据可视化
deepseek.com: pytorch可视化工具 生成神经网络图 在 PyTorch 中,可视化神经网络结构的常用工具和方法有以下几种,以下将详细介绍它们的用法: 1. TensorBoard (PyTorch 官方集成) PyTorch 通过 torch.utils.tensorboard 支持 TensorBoard&a…...
一周学会Flask3 Python Web开发-post请求与参数获取
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili app.route 装饰器默认只支持get请求。假如我们要让绑定的视图函数支持其他请求方式,我们可以在methods属性里配置…...

第3章 .NETCore核心基础组件:3.1 .NET Core依赖注入
3.1.1 什么是控制反转、依赖注入 杨老师在书中进行了一系列的文字阐述,总结一下就是:软件设计模式中有一种叫做【控制反转】的设计模式,而依赖注入是实现这种设计模式的一个很重要的方式。也就是说学习依赖注入,是学习怎样实现控…...

cs*n 网页内容转为html 加入 onenote
csdn上有好用的内容,我们怎么将它们加到 onenote 里吃灰呢。 一、创建 新html create_html.py import sysdef create_html_file(filename):# 检查是否提供了文件名if not filename:print("请提供HTML文件名")return# 创建HTML内容html_content f"…...

平板作为电脑拓展屏
有线串流(速度更快) spacedesk 打开usb对安卓的连接 用usb线直接连接电脑和平板 无线串流(延迟高,不推荐) todesk pc和手机端同时下载软件,连接后可以进行远程控制或扩展屏幕 spacedesk 连接到同一个…...
Pytorch实现论文之一种基于扰动卷积层和梯度归一化的生成对抗网络
简介 简介:提出了一种针对鉴别器的梯度惩罚方法和在鉴别器中采用扰动卷积,拟解决锐梯度空间引起的训练不稳定性问题和判别器的记忆问题。 论文题目:A Perturbed Convolutional Layer and Gradient Normalization based Generative Adversarial Network(一种基于扰动卷积层…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...