最小生成树MST-07 - jack

news/2025/9/20 16:50:15/文章来源:https://www.cnblogs.com/cavalier-chen/p/19102592

目录
  • 概念
  • Kruskal算法(克鲁斯卡尔算法)
  • Prim算法(普里姆算法)
  • 比较
  • MST应用场景
  • MST与 最短距离
  • MST与 TSP

参考链接
https://cloud.tencent.com/developer/article/1480529

概念

最小生成树MST
应该叫最小总间距树 Mininum Spanning Tree

最小生成树(MST,Minimum Spanning Tree)是图论中的经典问题。让我详细为你讲解MST的概念和两种经典算法。
什么是最小生成树?
最小生成树是连通加权无向图中一棵权重和最小的生成树。它具有以下特点:

  • 包含图中所有顶点
  • 是一棵树(n个顶点,n-1条边,无环)
  • 所有边的权重和最小

Kruskal算法(克鲁斯卡尔算法)

image

思想:贪心策略,按边权从小到大排序,依次加入不形成环的边。
步骤:
将所有边按权重从小到大排序
初始化并查集,每个顶点为一个独立的集合
遍历排序后的边,如果边连接的两个顶点不在同一集合中,则加入MST
重复直到MST包含n-1条边

class UnionFind:"""并查集数据结构"""def __init__(self, n):self.parent = list(range(n))self.rank = [0] * ndef find(self, x):"""查找根节点(路径压缩优化)"""if self.parent[x] != x:self.parent[x] = self.find(self.parent[x])return self.parent[x]def union(self, x, y):"""合并两个集合(按秩合并优化)"""px, py = self.find(x), self.find(y)if px == py:return Falseif self.rank[px] < self.rank[py]:px, py = py, pxself.parent[py] = pxif self.rank[px] == self.rank[py]:self.rank[px] += 1return Truedef kruskal_mst(n, edges):"""Kruskal算法求最小生成树参数:n: 顶点数量edges: 边的列表,每条边格式为 (权重, 顶点1, 顶点2)返回:(mst_edges, total_weight): MST的边列表和总权重"""# 按权重排序所有边edges.sort()uf = UnionFind(n)mst_edges = []total_weight = 0for weight, u, v in edges:# 如果u和v不在同一连通分量中,加入这条边if uf.union(u, v):mst_edges.append((u, v, weight))total_weight += weight# MST包含n-1条边时完成if len(mst_edges) == n - 1:breakreturn mst_edges, total_weight# 示例使用
if __name__ == "__main__":# 图的表示:顶点编号0到5,边格式为(权重, 起点, 终点)n = 6edges = [(4, 0, 1), (6, 0, 2), (6, 0, 3),(2, 1, 2), (5, 1, 4),(1, 2, 3), (5, 2, 4), (3, 2, 5),(4, 3, 5),(6, 4, 5)]mst_edges, total_weight = kruskal_mst(n, edges)print("Kruskal算法结果:")print("MST边:", mst_edges)print("总权重:", total_weight)# 可视化MSTprint("\nMST边详情:")for u, v, w in mst_edges:print(f"顶点 {u} -- 顶点 {v}:权重 {w}")

Prim算法(普里姆算法)

image

思想:从某个顶点开始,逐步扩展MST,每次选择连接MST和非MST顶点的最小权重边。
步骤:

选择起始顶点加入MST
维护一个优先队列,存储连接MST和非MST顶点的边
每次取出最小权重边,如果终点不在MST中,则加入MST
更新优先队列,加入新顶点的所有邻接边

import heapq
from collections import defaultdictdef prim_mst(n, edges, start=0):"""Prim算法求最小生成树参数:n: 顶点数量edges: 边的列表,每条边格式为 (权重, 顶点1, 顶点2)start: 起始顶点(默认为0)返回:(mst_edges, total_weight): MST的边列表和总权重"""# 构建邻接表graph = defaultdict(list)for weight, u, v in edges:graph[u].append((weight, v))graph[v].append((weight, u))# 初始化visited = set()mst_edges = []total_weight = 0min_heap = []  # 优先队列:(权重, 起点, 终点)# 从start顶点开始visited.add(start)# 将start的所有邻接边加入优先队列for weight, neighbor in graph[start]:heapq.heappush(min_heap, (weight, start, neighbor))while min_heap and len(mst_edges) < n - 1:weight, u, v = heapq.heappop(min_heap)# 如果终点已经在MST中,跳过这条边if v in visited:continue# 将边加入MSTvisited.add(v)mst_edges.append((u, v, weight))total_weight += weight# 将新顶点v的所有邻接边加入优先队列for next_weight, neighbor in graph[v]:if neighbor not in visited:heapq.heappush(min_heap, (next_weight, v, neighbor))return mst_edges, total_weightdef prim_mst_matrix(adj_matrix, start=0):"""Prim算法的邻接矩阵实现(经典版本)参数:adj_matrix: 邻接矩阵,adj_matrix[i][j]表示顶点i到j的权重,0表示无边start: 起始顶点返回:(mst_edges, total_weight): MST的边列表和总权重"""n = len(adj_matrix)visited = [False] * nmin_edge = [float('inf')] * n  # 到MST的最小边权重parent = [-1] * n  # 记录父节点# 起始顶点min_edge[start] = 0mst_edges = []total_weight = 0for _ in range(n):# 找到未访问顶点中min_edge最小的u = -1for v in range(n):if not visited[v] and (u == -1 or min_edge[v] < min_edge[u]):u = vvisited[u] = True# 如果不是起始顶点,添加边到MSTif parent[u] != -1:mst_edges.append((parent[u], u, adj_matrix[parent[u]][u]))total_weight += adj_matrix[parent[u]][u]# 更新相邻顶点的最小边权重for v in range(n):if not visited[v] and adj_matrix[u][v] > 0:if adj_matrix[u][v] < min_edge[v]:min_edge[v] = adj_matrix[u][v]parent[v] = ureturn mst_edges, total_weight# 示例使用
if __name__ == "__main__":# 使用边表示的图n = 6edges = [(4, 0, 1), (6, 0, 2), (6, 0, 3),(2, 1, 2), (5, 1, 4),(1, 2, 3), (5, 2, 4), (3, 2, 5),(4, 3, 5),(6, 4, 5)]mst_edges, total_weight = prim_mst(n, edges)print("Prim算法结果(邻接表版本):")print("MST边:", mst_edges)print("总权重:", total_weight)# 邻接矩阵版本adj_matrix = [[0, 4, 6, 6, 0, 0],[4, 0, 2, 0, 5, 0],[6, 2, 0, 1, 5, 3],[6, 0, 1, 0, 0, 4],[0, 5, 5, 0, 0, 6],[0, 0, 3, 4, 6, 0]]mst_edges2, total_weight2 = prim_mst_matrix(adj_matrix)print("\nPrim算法结果(邻接矩阵版本):")print("MST边:", mst_edges2)print("总权重:", total_weight2)# 可视化MSTprint("\nMST边详情:")for u, v, w in mst_edges:print(f"顶点 {u} -- 顶点 {v}:权重 {w}")

比较

Kruskal算法

时间复杂度:O(E log E),主要是边排序的时间
空间复杂度:O(V),并查集的空间
适用场景:稀疏图(边数相对较少)

Prim算法

时间复杂度:

使用优先队列:O(E log V)
使用邻接矩阵:O(V²)

空间复杂度:O(V + E)
适用场景:稠密图(边数较多)

算法选择建议

图较稀疏(E接近V):选择Kruskal算法
图较稠密(E接近V²):选择Prim算法
已有邻接矩阵:Prim算法更方便
需要在线算法:Prim算法可以逐步构建

核心思想对比

Kruskal:边的角度,全局最优选择
Prim:顶点的角度,局部扩展策略

MST应用场景

MST的主要应用场景

  1. 网络基础设施设计

电力网格:以最小成本连接所有发电站和变电站
通信网络:设计光纤骨干网,最小化铺设成本
供水系统:连接水源到所有用户的管道网络
交通网络:规划公路、铁路的基础连接

  1. 聚类分析

数据挖掘:基于特征相似性对数据进行分组
图像分割:将相似像素区域归为一类
社交网络:发现社区结构和用户群体
基因分析:基于序列相似性对基因进行分类

  1. 近似算法基础

TSP问题:提供2-近似解的理论保证
斯坦纳树问题:网络设计的扩展问题
设施选址:优化服务设施的布局

MST与 最短距离

核心区别:

优化目标不同:

MST:最小化所有连接的总成本
最短路径:最小化特定两点间的距离

解的结构不同:

MST:树结构(n-1条边,无环)
最短路径:路径序列

应用场景不同:

MST:网络设计、基础设施建设
最短路径:导航、路由、物流

MST对最短路径的帮助:

有限帮助:MST不能直接解决点到点最短路径
预处理作用:可以作为某些最短路径算法的预处理步骤
近似解:在某些特殊图中,MST路径可能接近最短路径

MST与 TSP

TSP 是旅行商问题 兜一圈回到出发点 访问所有的点 总路径最短

TSP最优解 ≥ MST权重
因为TSP路径去掉一条边就是生成树
算法选择建议
选择MST算法的场景:

✅ 需要连接所有节点且总成本最小
✅ 设计基础网络拓扑
✅ 进行数据聚类分析
✅ 构造复杂问题的近似解

选择最短路径算法的场景:
✅ 需要找到两点间最快/最短路径
✅ 网络路由和导航
✅ 物流配送路径优化
✅ 实时路径查询

MST是连接性问题的最优解,而最短路径是可达性问题的最优解。虽然它们解决的是不同类型的问题,但MST在很多复杂问题(如TSP)中扮演着重要的理论和实践角色。理解这些算法的适用场景和相互关系,有助于在实际问题中选择合适的解决方案。

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

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

相关文章

Java基础语法1

Java基础语法1 标识符 关键字 Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 所有的标识符都应该以字母(A-Z或a-z),美元符($),或者下划线(_)开始 首字符之后可以是字母(A-Z或a-z),…

makefile 入门2(变量赋值)

makefile 入门2(变量赋值)makefile 变量赋值 GNU make中赋值语法分为:= 将右侧的值赋值给左侧。延迟赋值 := 将右侧的值赋值给左侧。立即赋值 ::= 将右侧的值赋值给左侧。立即赋值 :::= 转义立即赋值运算符,右侧的…

JS复制并气泡提示

JS复制并气泡提示//气泡提示,仅css文件,不需要js文件 <link rel="stylesheet" href="css/tooltip.css" /> //复制内容,仅js文件,不需要css文件 <script src="js/clipboard.min.…

不定高元素动画实现方案(上)

最近接了一个需求,需要实现一个列表,列表可展开收起,展开收起需要有一个动画效果,而列表个数不定且每项内容高度也不固定,所以是一个不定高的收起展开效果,于是特意抽时间尝试了一些动画实现方案,特此记录前情 …

Python 潮流周刊#120:新型 Python 类型检查器对比(摘要)

本周刊由 Python猫 出品,精心筛选国内外的 400+ 信息源,为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景:帮助所有读者精进 Python 技术,并增长职业和副业的收入。 温馨提…

精选HTML、JavaScript、ASP代码片段集锦

使用SCP(Secure Copy Protocol)在CentOS 7操作系统上向远程服务器传输文件是一个安全且常用的方法。SCP工具基于SSH (Secure Shell) 协议,能保证传输过程的安全性。以下为在CentOS 7上使用SCP命令向目标服务器传输文…

详细介绍:Node.js中Express框架入门教程

详细介绍:Node.js中Express框架入门教程2025-09-20 16:38 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block…

实用指南:【鸿蒙面试题-6】LazyForEach 懒加载

实用指南:【鸿蒙面试题-6】LazyForEach 懒加载pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

基于LlamaIndex的相似性搜索

🌟 引言:为什么需要“相似性搜索”? 在信息爆炸的时代,我们每天面对大量文档、笔记、技术手册、会议记录……如何快速从自己的“知识库”中找到最相关的内容? 传统关键词搜索(如 Ctrl+F)已经不够用了 —— 它无…

第二周预习报告(AI)

AI对学习内容的总结 使用AI工具: 腾讯元宝 (https://yuanbao.tencent.com/) 总结内容: 本周学习聚焦于C语言高级特性、对称密码算法实现及密码学编码格式。 Head First C第8章:静态与动态库核心思想: 代码复用与模…

编写代码时遇到的checkstyle问题归纳

当开发者编写代码时,遵循一定的代码风格和规范是必要的,这不仅有助于提高代码质量,也使代码更易于阅读和维护。Checkstyle 是一个用于检查Java源代码的工具,它帮助开发者按照一定的编码标准进行编程。它可以集成在…

.netcore的Lucene.Net基础应用

.netcore的Lucene.Net基础应用最近研究了一下使用.netcore的Lucene.Net应用,整理一下研究内容。 一、研究原因 1、系统环境:多终端、多服务、达梦数据库8、.netcore7.0、多机负载。 2、业务数量:单表1000万条数据,…

rook-ceph CRD资源配置时效困难

rook-ceph CRD资源配置时效困难2025-09-20 16:05 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

实用指南:conda常见问题

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

关于1200模拟量输入滤波的问题

参考:为什么使用S7-1200模拟量输入模块时接收到变动很大的不稳定的值?unstable 参考:关于模拟量输入滤波的问题-SIMATIC S7-1500系列-找答案-西门子中国滤波周期设置和模拟量输入反馈速率有关!!! 调整为无时,可…

在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤

由于Ubuntu 16.04的标准库可能不包含更早版本的OpenJDK,你可能需要添加额外的存储库或者下载特定的安装包。对于OpenJDK的这些旧版本,你可以使用Third-party PPA(个人软件包归档),或者从OpenJDK档案站点下载相应的…

物流行业信息咨询智能问答系统

物流行业信息咨询智能问答系统2025-09-20 15:58 dribs 阅读(0) 评论(0) 收藏 举报背景 练手rag项目 LLM都是基于过去的经验数据进行训练完成;无法处理获取实时的信息,需结合RAG实现;处理私域的数据 流程原理项目…

插座(SOCKET)

程序用来“上网说话”的工具。1:什么是套接字? 应用程序与网络协议栈之间进行数据收发的编程接口(API) 2.如何理解? 应用程序需要套接字才能接入网络,与其他计算机上的程序进行通信(插座---->电器需要插入插…

线性代数 行列式 | 子式 / 主子式 / 顺序主子式 / 余子式 / 代数余子式 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …