两种最小生成树算法讲解

prim算法精讲

卡码网53. 寻宝

        本题题目内容为最短连接,是最小生成树的模板题,那么我们来讲一讲最小生成树。最小生成树可以使用prim算法也可以使用kruskal算法计算出来。本篇我们先讲解prim算法。

        最小生成树是所有节点的最小连通子图,即:以最小的成本(边的权值)将图中所有节点链接到一起。图中有n个节点,那么一定可以用n-1条边将所有节点连接到一起。

        prim算法是从节点的角度采用贪心的策略每次寻找距离最小生成树最近的节点并加入到最小生成树中。

        prim算法核心就是三步,我称为prim三部曲,大家一定要熟悉这三步,代码相对会好些很多:

  1. 第一步,选距离生成树最近节点
  2. 第二步,最近节点加入生成树
  3. 第三步,更新非生成树节点到生成树的距离(即更新minDist数组)

        同时,因为使用一维数组,数组的下标和数组如何赋值很重要,不要搞反,导致结果被覆盖。

        一定要理解minDist数组,简单说就是一棵树不断记录离自己最近的节点,并吸收进来,再不断循环这个过程

        不断吸收新节点,不断以树外节点离树最近的距离做更新,并更新树,循环,最后达到下图效果,具体过程为绿色部分不断延伸(即minDist数组不断更新),最终计算数组和即可

代码实现

        记录边,注意初始化数组大小

def prim(v, e, edges):"""Prim算法实现,用于求解无向连通图的最小生成树(MST)总权重参数:v: 顶点数量e: 边的数量edges: 边的列表,每个元素为元组(x, y, k),表示顶点x与y之间的边权重为k返回:最小生成树的总权重"""import sysimport heapq  # 虽然导入了heapq但未使用,可能是预留或之前版本的残留# 初始化邻接矩阵,大小为(v+1)x(v+1)(顶点编号从1开始)# 初始值设为10001表示无穷大(假设所有边的权重都小于此值)grid = [[10001] * (v + 1) for _ in range(v + 1)]# 读取边的信息并填充邻接矩阵# 由于是无向图,x到y和y到x的权重相同for edge in edges:x, y, k = edge  # x和y为两个顶点,k为边的权重grid[x][y] = kgrid[y][x] = k# minDist数组:记录每个非树节点到生成树的最短距离# 索引0未使用(顶点编号从1开始)minDist = [10001] * (v + 1)# 从顶点1开始构建生成树,因此将其到生成树的距离设为0minDist[1] = 0# isInTree数组:标记顶点是否已加入生成树# True表示已加入,False表示未加入isInTree = [False] * (v + 1)# Prim算法主循环:需要加入v-1条边(构建包含所有v个顶点的树)for i in range(1, v):# 1. 找到当前未加入生成树且距离最近的顶点cur = -1  # 存储当前选中的顶点minVal = sys.maxsize  # 存储当前最小距离(初始为系统最大整数)# 遍历所有顶点,寻找符合条件的顶点for j in range(1, v + 1):# 条件:未加入生成树 且 距离生成树更近if not isInTree[j] and minDist[j] < minVal:minVal = minDist[j]  # 更新最小距离cur = j  # 更新选中的顶点# 2. 将找到的最近顶点加入生成树isInTree[cur] = True# 3. 更新所有未加入生成树的顶点到生成树的最短距离# 以刚加入的cur顶点为中间点,检查是否有更短路径for j in range(1, v + 1):# 条件:未加入生成树 且 通过cur顶点能获得更短距离if not isInTree[j] and grid[cur][j] < minDist[j]:minDist[j] = grid[cur][j]  # 更新最短距离# parent[j] = cur; // 记录最小生成树的边 (注意数组指向的顺序很重要)# 计算最小生成树的总权重:累加顶点2到v的最小距离(顶点1为起点,距离0)result = sum(minDist[2:v+1])return resultif __name__ == "__main__":"""程序入口:读取输入数据并调用prim函数计算结果"""import sys# 读取所有输入数据(一次性读取提高效率,适用于大数据量)input = sys.stdin.readdata = input().split()  # 将输入按空白字符分割为列表# 解析顶点数和边数(前两个元素)v = int(data[0])e = int(data[1])# 解析所有边的信息edges = []index = 2  # 从第三个元素开始是边的信息for _ in range(e):x = int(data[index])y = int(data[index + 1])k = int(data[index + 2])edges.append((x, y, k))  # 将边信息存入列表index += 3  # 移动到下一条边的起始位置# 调用prim算法计算结果并输出result = prim(v, e, edges)print(result)

kruskal算法精讲

        通过贪心的思路,从边出发,(在是否同一集合的情况下判断)不断吸收最小权值的边。再次注意本题是要求连接所有节点!!

        但在代码中,如果将两个节点加入同一个集合,又如何判断两个节点是否在同一个集合呢?我们在并查集开篇的时候就讲了,并查集主要就两个功能:

  • 将两个元素添加到一个集合中
  • 判断两个元素在不在同一个集合

代码实现

        本题图示是绿色线条的不断延伸(每次多一条边),上题为以节点为中心不断更新最短距离数组

        lambda 是 Python 中的匿名函数,语法为 lambda 参数: 表达式,通常用于简化代码,lambda 中的 edge 是一个形参sort 方法在遍历 edges 时会自动将列表中的每个元素作为实参传给它,因此 edge 能依次指向 edges 中的所有元素

# 定义边(Edge)类,用于存储一条边的信息
class Edge:def __init__(self, l, r, val):self.l = l      # 边的左顶点self.r = r      # 边的右顶点self.val = val  # 边的权重值# 初始化常量和并查集数组
n = 10001               # 最大顶点数限制
father = list(range(n)) # 并查集数组,father[u]表示u的父节点def init():"""初始化并查集,让每个节点的父节点都是自己"""global fatherfather = list(range(n))def find(u):"""查找节点u的根节点,同时进行路径压缩路径压缩是为了加快后续的查询速度"""if u != father[u]:# 递归查找根节点,并将当前节点的父节点直接指向根节点father[u] = find(father[u])return father[u]def join(u, v):"""合并两个集合:将节点v所在的集合合并到节点u所在的集合前提是u和v已经是各自集合的根节点"""u = find(u)v = find(v)if u != v:# 将v的父节点设置为u,实现两个集合的合并father[v] = udef kruskal(v, edges):"""Kruskal算法实现:求解最小生成树的总权重参数:v: 顶点数量edges: 边的列表,每个元素是Edge对象返回:最小生成树的总权重"""# 1. 按边的权重从小到大排序(核心步骤)edges.sort(key=lambda edge: edge.val)# 2. 初始化并查集init()# 3. 存储最小生成树的总权重result_val = 0# 4. 遍历所有排序后的边,尝试加入生成树for edge in edges:# 查找两个顶点所在集合的根节点x = find(edge.l)y = find(edge.r)# 如果根节点不同,说明不在同一个集合,不会形成环if x != y:# 将这条边加入最小生成树result_val += edge.val# 合并两个集合join(x, y)return result_valif __name__ == "__main__":"""程序入口:读取输入并调用Kruskal算法计算结果"""import sys# 一次性读取所有输入数据input = sys.stdin.readdata = input().split()  # 按空白字符分割成列表# 解析顶点数和边数v = int(data[0])e = int(data[1])# 解析所有边的信息edges = []index = 2  # 从第三个元素开始是边的信息for _ in range(e):v1 = int(data[index])v2 = int(data[index + 1])val = int(data[index + 2])edges.append(Edge(v1, v2, val))  # 创建Edge对象并添加到列表index += 3  # 移动到下一条边的起始位置# 调用Kruskal算法计算结果并输出result_val = kruskal(v, edges)print(result_val)

拓展

        在节点数量固定的情况下,图中的边越少,Kruskal 需要遍历的边也就越少。而 prim 算法是对节点进行操作的,节点数量越少,prim算法效率就越优。所以在 稀疏图中,用Kruskal更优。 在稠密图中,用prim算法更优。

        为什么边少的话,使用 Kruskal 更优呢因为 Kruskal 是对边进行排序的后 进行操作是否加入到最小生成树。边如果少,那么遍历操作的次数就少。

        Prim 算法 时间复杂度为 O(n^2),其中 n 为节点数量,它的运行效率和图中边树无关,适用稠密图。

        Kruskal算法 时间复杂度 为 nlogn,其中n 为边的数量,适用稀疏图

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

148-基于Python的2024物流年度销售收入数据可视化分析系统

基于Python Django的物流数据可视化分析系统开发实录 项目背景 随着物流行业数据量的激增&#xff0c;企业对数据分析和可视化的需求日益增长。传统的Excel分析方式难以满足多维度、实时、交互式的数据洞察需求。为此&#xff0c;我们开发了一个基于Python Django的物流年度销售…

Python中的关键字参数:灵活与可读性的完美结合(Effective Python 第23条)

在Python编程中&#xff0c;函数参数的传递方式灵活多样&#xff0c;而其中一种特别强大的方式就是关键字参数。关键字参数不仅能够提升代码的可读性&#xff0c;还为函数的设计和调用提供了极大的便利。本文将深入探讨关键字参数的用法、优势以及实际应用中的注意事项。 一、关…

005.Redis 主从复制架构

主从复制概念与原理 核心概念 主节点&#xff08;Master&#xff09;&#xff1a;唯一接受写操作的节点&#xff0c;数据修改后异步复制到从节点。 从节点&#xff08;Replica&#xff09;&#xff1a;复制主节点数据的节点&#xff0c;默认只读&#xff08;可配置为可写但不…

Android Studio 模拟器 “******“ has terminated 问题

问题&#xff1a;Android Studio 模拟器 "**" has terminated 问题设备信息&#xff1a;CPU:I5 7500U RAM:64GB System:Windows 10 64位解决&#xff1a; 网上所有办法都尝试后仍然不可行可尝试如下办法&#xff1a;1、此电脑→管理→设备管理→显示适配器→右击→…

uniapp 懒加载图片

实现的功能 1.一次性获取图片。 2.按用户视野范围内看到的图片滚动下来进行懒加载,提高浏览器性能。 3.不要一次性加载全部的图片 1.给父组件绑定一个滚动监听 1.页面路径:/pages/Home/index.vue 不在一个页面的话用 EventBus去触发。@scroll="handleScroll2" Ev…

Android - 资源类型 MINE Type

一、概念MINE&#xff08;Multipurpose Internet Mail Extensions&#xff09;最初是为了标识电子邮件附件的类型&#xff0c;在 HTML 中使用 content-type 属性表示&#xff0c;描述了文件类型的互联网标准。格式&#xff1a;媒体类型/子类型&#xff0c;可使用通配符*。如 au…

php8.+ 新函数总结

PHP系统函数是PHP核心提供的内置函数&#xff0c;用于执行常见任务&#xff0c;如字符串操作、数组处理、数学运算等。它们通过预定义代码块封装了特定功能&#xff0c;开发者可直接调用而无需重复编写代码。 而 PHP 8.0以后又新增了一些实用函数&#xff0c;今天总结部分常见的…

Qt事件处理机制详解

一、事件处理基本流程在Qt中&#xff0c;所有从QObject派生的类都能处理事件。事件处理的核心流程如下&#xff1a;事件入口函数&#xff1a;bool QObject::event(QEvent *e)参数e包含事件信息&#xff0c;通过e->type()获取事件类型返回值true表示事件已被处理&#xff0c;…

Zynq中级开发七项必修课-第三课:S_AXI_GP0 主动访问 PS 地址空间

Zynq中级开发七项必修课-第三课&#xff1a;S_AXI_GP0 主动访问 PS 地址空间 目标1.0 编写 AXI-Lite Master&#xff1a;按键计数 → 写入 PS 内存1.1 PL 触发中断 → PS 响应并串口打印按键计数值BD图axi_lite_master.v // // AXI4-Lite Simple Master (single-shot, non-pip…

CVPR | 2025 | MAP:通过掩码自回归预训练释放混合 Mamba - Transformer 视觉骨干网络的潜力

文章目录CVPR | 2025 | MAP&#xff1a;通过掩码自回归预训练释放混合 Mamba - Transformer 视觉骨干网络的潜力创新点初步研究初步结论方法确定一个混合网络方法掩码机制掩码比例MAP的transformer解码器重建目标实验ImageNet-1k 上的 2D 分类CVPR | 2025 | MAP&#xff1a;通过…

Spring Boot + Spring AI 最小可运行 Demo

一. 项目依赖&#xff08;pom.xml&#xff09;<project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/mav…

AI重塑校园教育:中小学AI智慧课堂定制方案+AI作业批改减负,告别一刀切学生进步快

家长们&#xff0c;你有没有听过孩子抱怨上学的烦恼&#xff1f;课堂上老师讲的内容&#xff0c;有的同学觉得太简单 “吃不饱”&#xff0c;有的却跟不上 “听不懂”&#xff1b;放学后作业堆成山&#xff0c;老师要熬夜批改到半夜&#xff0c;错题反馈要等第二天才能拿到&…

旧物循环,交易新生——旧物回收二手交易小程序,引领绿色消费新风尚

在资源日益紧张、环境污染问题日益突出的今天&#xff0c;绿色消费已经成为时代发展的必然趋势。旧物回收二手交易小程序&#xff0c;作为绿色消费的重要载体&#xff0c;正以其独特的优势和魅力&#xff0c;引领着一场关于旧物循环、交易新生的绿色革命。一、旧物循环&#xf…

刷机维修进阶教程-----如何清除云账号 修复wifi 指南针 相机 指纹等刷机故障

在刷机、系统升级或降级过程中,是否遇到过以下问题:WiFi无法开启、相机无响应、指南针或陀螺仪失灵 指纹等故障?另外,云账号是否仍会保留,即使通过9008模式刷机也无法彻底清除?那么这篇博文都可以找到答案。 通过博文了解💝💝💝 1💝💝💝----云账号信息分区如…

AI翻唱实战:用[灵龙AI API]玩转AI翻唱 – 第6篇

历史文章 [灵龙AI API] 申请访问令牌 - 第1篇 [灵龙AI API] AI生成视频API&#xff1a;文生视频 – 第2篇 图生视频实战&#xff1a;用[灵龙AI API]玩转AI生成视频 – 第2篇&#xff0c;从静图到大片 单图特效实战&#xff1a;用[灵龙AI API]玩转AI生成视频 – 第3篇&#…

大模型0基础开发入门与实践:第11章 进阶:LangChain与外部工具调用

第11章 进阶&#xff1a;LangChain与外部工具调用 1. 引言 在上一章&#xff0c;我们成功地创造了我们的第一个“生命”——一个可以对话的机器人。我们为它的诞生而兴奋&#xff0c;但很快我们就会发现它的局限性。它就像一个被囚禁在玻璃房中的天才大脑&#xff0c;拥有渊博…

SQL 日期处理:深入解析与高效实践

SQL 日期处理&#xff1a;深入解析与高效实践 引言 在数据库管理中&#xff0c;日期和时间数据的处理是不可或缺的一部分。SQL&#xff08;结构化查询语言&#xff09;提供了丰富的日期和时间函数&#xff0c;使得对日期的处理变得既灵活又高效。本文将深入探讨SQL日期处理的相…

源代码部署 LAMP 架构

源代码部署 LAMP 架构 &#xff08;Linux Apache MySQL PHP&#xff09; 一、LAMP 架构概述 LAMP 是一套经典的开源 Web 服务架构&#xff0c;通过源代码安装可实现高度定制化&#xff0c;适用于对软件版本、功能模块有特定需求的场景。本指南基于 CentOS 7 系统&#xf…

GO环境变量中GO111MODULE到底是干啥的?

查看GO111MODULE变量GO111MODULE的作用GO111MODULE的案例演示 一&#xff0c;查看GO111MODULE变量 ]# go env GO111MODULE 或者 ]# go env | grep GO111MODULE二&#xff0c;GO111MODULE的作用 auto : 自动判断机制 当项目位于 $GOPATH/src 目录外且包含 go.mod 文件时&…

在线培训机构如何降低培训视频被盗录的风险

每一节精心录制的培训视频&#xff0c;都凝聚着讲师的心血与机构的巨大投入。然而&#xff0c;只需一个简单的录屏软件&#xff0c;这一切都可能被轻易窃取。一旦被盗取&#xff0c;不但会损失经济利益&#xff0c;还可能会影响机构的声誉。那么&#xff0c;在线培训机构如何降…