最小生成树算法详解

    • 一、最小生成树基础概念
      • 1.1 生成树与最小生成树
      • 1.2 核心性质
      • 1.3 应用场景
    • 二、Prim 算法:从顶点出发的“生长式”构建
      • 2.1 算法原理
      • 2.2 Java 代码实现(邻接矩阵版)
      • 2.3 复杂度分析
    • 三、Kruskal 算法:按边权排序的“选边式”构建
      • 3.1 算法原理
      • 3.2 Java 代码实现(并查集+边排序)
      • 3.3 复杂度分析
    • 四、两种算法的对比与选择
    • 五、最小生成树的应用与拓展
      • 5.1 经典应用
      • 5.2 实际问题中的优化

图论与网络优化中,最小生成树(Minimum Spanning Tree,MST)是一类重要问题,它能在连接所有节点的前提下,找到总权重最小的边集合,广泛应用于通信网络布线、管道铺设、电路设计等场景(核心需求是“用最少成本连接所有节点”)。

一、最小生成树基础概念

1.1 生成树与最小生成树

  • 生成树:对于一个有 n 个节点的连通图,生成树是包含所有 n 个节点且恰好有 n-1 条边的子图,且子图中无环(保证连通性的同时无冗余边)。
  • 最小生成树:在带权连通图中,所有生成树中总边权之和最小的生成树称为最小生成树。

1.2 核心性质

  • 连通性:包含原图所有节点,任意两点之间有且仅有一条路径。
  • 无环性:恰好有 n-1 条边,若再添加一条边必形成环。
  • 最小性:总边权之和在所有可能的生成树中最小。

1.3 应用场景

  • 通信网络布线:用最少的线缆连接所有城市。
  • 电路设计:在芯片中用最短的导线连接所有元件。
  • 管道铺设:以最低成本修建管道连接所有工厂。
  • 聚类分析:通过最小生成树的边权划分数据簇。

二、Prim 算法:从顶点出发的“生长式”构建

Prim 算法的核心是“从一个起点开始,逐步添加边以连接新节点,始终保持子图无环且总权最小”。

2.1 算法原理

  1. 初始化

    • 选择任意节点作为起点(如节点 0),标记为“已加入生成树”。
    • 维护一个 lowCost 数组:lowCost[i] 表示未加入生成树的节点 i 与生成树中节点的最小边权(若无边连接则为 +∞)。
  2. 迭代选择

    • lowCost 中选择权值最小的边,对应节点 u 加入生成树。
    • 更新 lowCost 数组:对所有未加入生成树的节点 v,若边 u-v 的权值小于 lowCost[v],则更新 lowCost[v] 为该权值(因为 u 已加入生成树,v 到生成树的最小边可能变为 u-v)。
  3. 终止条件

    • 当生成树包含所有 n 个节点(共选择 n-1 条边),算法结束。
      prim算法

2.2 Java 代码实现(邻接矩阵版)

public class PrimMST {// 邻接矩阵:graph[i][j]表示边i-j的权值,0表示无直接连接public int[] prim(int[][] graph) {int n = graph.length;int[] parent = new int[n]; // parent[i]记录生成树中i的父节点(用于还原树)int[] lowCost = new int[n]; // 未加入节点到生成树的最小边权boolean[] inMST = new boolean[n]; // 标记节点是否已加入生成树// 初始化:起点为0lowCost[0] = 0;parent[0] = -1; // 起点无父节点for (int i = 1; i < n; i++) {lowCost[i] = graph[0][i] == 0 ? Integer.MAX_VALUE : graph[0][i];parent[i] = 0;}inMST[0] = true;// 共需要加入n-1个节点for (int count = 1; count < n; count++) {// 步骤1:选择lowCost中权值最小的未加入节点uint u = -1;int min = Integer.MAX_VALUE;for (int i = 0; i < n; i++) {if (!inMST[i] && lowCost[i] < min) {min = lowCost[i];u = i;}}if (u == -1) {// 图不连通,无法生成MSTreturn null;}inMST[u] = true; // 加入生成树// 步骤2:更新lowCost数组for (int v = 0; v < n; v++) {if (!inMST[v] && graph[u][v] != 0 && graph[u][v] < lowCost[v]) {lowCost[v] = graph[u][v];parent[v] = u;}}}return parent; // parent数组记录生成树的边(v-parent[v])}// 测试public static void main(String[] args) {// 邻接矩阵表示带权图(0表示无边)int[][] graph = {{0, 2, 0, 6, 0},{2, 0, 3, 8, 5},{0, 3, 0, 0, 7},{6, 8, 0, 0, 9},{0, 5, 7, 9, 0}};PrimMST prim = new PrimMST();int[] parent = prim.prim(graph);if (parent != null) {System.out.println("Prim生成树的边(子节点-父节点):");int totalWeight = 0;for (int i = 1; i < parent.length; i++) {int weight = graph[i][parent[i]];System.out.println(i + "-" + parent[i] + "(权值:" + weight + ")");totalWeight += weight;}System.out.println("总权值:" + totalWeight); // 输出16(2+3+5+6)} else {System.out.println("图不连通,无法生成生成树");}}
}

2.3 复杂度分析

  • 邻接矩阵实现:时间复杂度为 O(n²)n 为节点数),适合稠密图(边数接近 )。
  • 邻接表+优先队列:优化后时间复杂度为 O(m log n)m 为边数),适合稀疏图。
  • 空间复杂度O(n)(存储 lowCostparent 等数组)。

三、Kruskal 算法:按边权排序的“选边式”构建

Kruskal 算法的核心是“按边权从小到大选择边,避免形成环,直到连接所有节点”。

3.1 算法原理

  1. 初始化

    • 将所有边按权值从小到大排序。
    • 初始化并查集(Union-Find):每个节点各自为一个集合(用于检测环)。
    • 维护一个边集合,用于存储生成树的边。
  2. 选边与合并

    • 按排序后的顺序遍历边 (u, v)
      • uv 不在同一个集合(无环),则将该边加入生成树,并合并 uv 所在的集合。
      • uv 在同一个集合(会形成环),则跳过该边。
  3. 终止条件

    • 当生成树的边数达到 n-1(连接所有节点),算法结束。
      Kruskal算法

3.2 Java 代码实现(并查集+边排序)

import java.util.*;// 边的表示(起点,终点,权值)
class Edge implements Comparable<Edge> {int u, v, weight;public Edge(int u, int v, int weight) {this.u = u;this.v = v;this.weight = weight;}@Overridepublic int compareTo(Edge other) {return Integer.compare(this.weight, other.weight); // 按权值升序排序}
}// 并查集(用于检测环)
class UnionFind {private int[] parent;public UnionFind(int n) {parent = new int[n];for (int i = 0; i < n; i++) {parent[i] = i; // 初始化:每个节点的父节点是自己}}// 查找根节点(带路径压缩)public int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]); // 路径压缩:缩短后续查找路径}return parent[x];}// 合并两个集合(按秩合并优化)public boolean union(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX == rootY) {return false; // 已在同一集合(会形成环)}parent[rootY] = rootX; // 合并return true;}
}public class KruskalMST {public List<Edge> kruskal(int n, List<Edge> edges) {List<Edge> mst = new ArrayList<>();UnionFind uf = new UnionFind(n);// 步骤1:按权值从小到大排序边Collections.sort(edges);// 步骤2:遍历边,选边并避免环for (Edge edge : edges) {if (mst.size() == n - 1) {break; // 已选够n-1条边,生成树完成}int u = edge.u;int v = edge.v;if (uf.union(u, v)) { // 若u和v不在同一集合(无环)mst.add(edge);}}// 若边数不足n-1,说明图不连通return mst.size() == n - 1 ? mst : null;}// 测试public static void main(String[] args) {int n = 5; // 5个节点List<Edge> edges = new ArrayList<>();edges.add(new Edge(0, 1, 2));edges.add(new Edge(0, 3, 6));edges.add(new Edge(1, 2, 3));edges.add(new Edge(1, 3, 8));edges.add(new Edge(1, 4, 5));edges.add(new Edge(2, 4, 7));edges.add(new Edge(3, 4, 9));KruskalMST kruskal = new KruskalMST();List<Edge> mst = kruskal.kruskal(n, edges);if (mst != null) {System.out.println("Kruskal生成树的边:");int totalWeight = 0;for (Edge edge : mst) {System.out.println(edge.u + "-" + edge.v + "(权值:" + edge.weight + ")");totalWeight += edge.weight;}System.out.println("总权值:" + totalWeight); // 输出16(2+3+5+6)} else {System.out.println("图不连通,无法生成生成树");}}
}

3.3 复杂度分析

  • 时间复杂度O(m log m)(主要来自边的排序,m 为边数),适合稀疏图(边数少)。
  • 空间复杂度O(n + m)(存储边、并查集等)。

四、两种算法的对比与选择

特性Prim 算法Kruskal 算法
核心思想从节点出发,生长式扩展从边出发,选边式构建
关键数据结构邻接矩阵/优先队列并查集+排序边
时间复杂度稠密图 O(n²),稀疏图 O(m log n)O(m log m)(依赖边排序)
适用场景稠密图(边多节点少)稀疏图(边少节点多)
优势无需排序边,适合节点少的图边排序后逻辑简单,适合边少的图

五、最小生成树的应用与拓展

5.1 经典应用

  • 次小生成树:在最小生成树基础上,替换一条边得到的总权值次小的生成树,用于容错设计(某条边失效时的备用方案)。
  • 多源最小生成树:连接多个起点的最小生成树,适用于“多中心网络”设计(如多个数据中心连接所有城市)。

5.2 实际问题中的优化

  • 带限制的最小生成树:如“最多使用 k 条某类边”,可在选边时添加约束。
  • 动态图的最小生成树:当图中边权或节点动态变化时,高效更新生成树(避免重新计算)。

总结
最小生成树是解决“低成本连接所有节点”问题的核心工具,Prim 和 Kruskal 是两种经典实现:

  • Prim 适合稠密图,通过“生长式”扩展节点,用 lowCost 数组跟踪最小边。
  • Kruskal 适合稀疏图,通过“选边式”添加边,用并查集避免环。
    实际应用中,需根据图的稠密程度选择算法:稠密图优先用 Prim(邻接矩阵实现),稀疏图优先用 Kruskal(边排序+并查集)。掌握这两种算法,能有效解决网络布线、聚类分析等实际问题。

That’s all, thanks for reading~~
觉得有用就点个赞、收进收藏夹吧!关注我,获取更多干货~

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

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

相关文章

YOLO 目标检测的改进方法

YOLO目标检测的改进方法可以从模型架构、训练策略、损失函数等多个方面入手&#xff0c;以下是一些常见的改进方法方向及参考文献&#xff1a; 模型架构改进 骨干网络替换&#xff1a;使用更轻量或更强大的网络替换原始骨干网络。轻量级网络如MobileNetV3、ShuffleNetV2等适合…

C++ 程序 AddressSanitizer:DEADLYSIGNAL

GCC && G 操作系统&#xff1a;Ubuntu 22.04 现象&#xff1a;C程序编译时开启ASAN&#xff0c;运行时有几率会出现大量AddressSanitizer:DEADLYSIGNAL 参考文章&#xff1a; https://stackoverflow.com/questions/77894856/possible-bug-in-gcc-sanitizers https://st…

【强化学习】实际部署

环境 Gymnasium 作为环境接口&#xff0c; PyBullet作为物理仿真平台&#xff0c; Stable Baselines3 用于训练算法。 测试框架搭建 以pybullet自带的Cart-pole-v1为例 安装依赖&#xff1a;确保安装了 Gymnasium 和 SB3 ( pip install gymnasium stable-baselines3 ).初始化环…

集训Demo4

创建数据库创建项目基本和视频中的一样我给User添加了vip这个属性&#xff0c;想实现两个令牌通过访问的案例&#xff0c;但遇到了问题一个令牌是密码加用户名的map数组这是它的获取、验证逻辑获取验证另一个令牌是Int vip这是自己写的另一套密钥和方法获取但在验证这里有问题头…

深度优化:Java 慢查询排查与性能调优实战

文章目录&#x1f680; 深度优化&#xff1a;Java 慢查询排查与性能调优实战&#x1f6a8;1. 事故全景&#xff1a;从告警到定位&#x1f575;️‍♂️1.1 事故时间线&#x1f4ca; 1.2 关键指标异常&#x1f6e0;️ 1.3 排查工具链&#x1f50d; 2. 深度剖析&#xff1a;MySQL…

TF-IDF(Term Frequency - Inverse Document Frequency)

TF-IDF&#xff08;Term Frequency - Inverse Document Frequency&#xff09;是一种在信息检索与文本挖掘中非常常用的关键词提取方法&#xff0c;用于衡量一个词在文档集合中的重要性。它的核心思想是&#xff1a;如果一个词在某个文档中出现得频繁&#xff0c;同时在其他文档…

Chrome紧急更新,谷歌修复正遭活跃利用的关键零日漏洞

谷歌已针对桌面版Chrome发布重要稳定渠道更新&#xff08;版本138.0.7204.157/.158&#xff09;&#xff0c;修复了六个安全漏洞&#xff0c;其中包括一个已被实际利用的漏洞。该更新正在向Windows、Mac和Linux平台推送&#xff0c;预计未来数日或数周内将通过自动更新完成部署…

Typecho插件开发:实现文章字数统计与阅读时长计算功能

文章目录 Typecho文章字数统计与阅读时长计算功能实现指南 1. 功能背景与需求分析 2. 插件设计与实现 2.1 插件基础结构 2.2 插件主逻辑实现 2.3 代码解析与优化 3. 前端展示优化 3.1 CSS样式增强 3.2 多语言支持 4. 高级功能扩展 4.1 数据库表优化 4.2 定时批量处理历史文章 5…

开源短链接工具 Sink 无需服务器 轻松部署到 Workers / Pages

本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 Sink 是一款开源免费的短链接生成工具,支持自定义短链接 Slug 以及设置到期时间,并且还可以借助 Cloudflare 的 Analytics Engine 功能分析短链接的统计数据。 最重要的是实现以上这些功能并不需要有自己的服务器,…

嵌入式数据结构之顺序表总结

以下是为嵌入式面试准备的顺序表全面优化指南&#xff0c;结合高频考点、代码规范与嵌入式专项优化技巧&#xff0c;助你系统掌握该知识点。 一、顺序表基础与嵌入式特点 ​本质​ 用连续内存空间存储线性表元素&#xff0c;通过下标实现O(1)随机访问 。 ​嵌入式优势​&#x…

Pytorch下载Mnist手写数据识别训练数据集的代码详解

datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransforms.ToTensor())1. datasets.MNIST这是torchvision.datasets模块中的一个类&#xff0c;专门用于加载MNIST数据集。MNIST是一个著名的手写数字识别数据集&#xff0c;包含60,000个训练样本和10,000个测试…

汽车免拆诊断案例 | 07款丰田Hilux启动故障

故障现象一辆 2007 年的丰田Hilux 2.5L柴油手动挡&#xff0c;行驶里程为23万公里。车主说车辆有很多故障&#xff0c;包括故障灯闪烁、发动机启动后又熄火、短时间运行时发动机还会剧烈抖动异响&#xff0c;从排气管冒出大量烟雾。故障诊断接车之后进行检查&#xff0c;发现发…

黄老师(Exeter University)学术交流

1. 文章结构与核心贡献聚焦 强调明确切入点和核心“亮点”贡献&#xff0c;避免分散&#xff0c;确保至少一项最主要、富有创新的方法。在该贡献点上进行全面充分的实验验证&#xff0c;包括不同模型尺寸、普适性测试&#xff0c;以应对审稿专家的质疑。建议从读者或审稿人角度…

ArcGIS Pro+PS 实现地形渲染效果图

先前关注了B站和小红书博主&#xff0c;设计暴风眼&#xff0c;大神讲的确实好&#xff0c;深感佩服&#xff0c;自己以前的制图仅仅实现了制图&#xff0c;实现了把图放在论文里能凑合&#xff0c;而不是设计。最近抽时间学习了一下大神的合集&#xff1a;ArcGIS Pro实用技法合…

ollma dify 搭建合同审查助手

目录 windows dify: ollma 配置 ollma下载地址&#xff1a; qwen3 模型下载 这个自动下载&#xff0c;下载后自动运行。 配置环境变量&#xff1a;修改监听后很慢 测试命令&#xff1a; 模型配置url&#xff1a; 搭建工作流 windows dify: 下载 dify代码&#xff1a…

解锁 iOS 按键精灵辅助工具自动化新可能:iOSElement.Click 让元素交互更简单

在移动自动化测试与脚本开发领域&#xff0c;精准操控应用元素是核心需求。无论是自动化测试流程、批量操作处理&#xff0c;还是场景化脚本开发&#xff0c;能否可靠地点击指定元素直接决定了自动化任务的成败。在 iOS 自动化操作中&#xff0c;开发者常常面临三大痛点&#x…

【机器学习】AdamW可调参数介绍及使用说明

在 AdamW 算法中调整参数对模型训练过程和最终效果有直接且重要的影响&#xff0c;以下是各关键参数对性能的具体影响总结&#xff1a;AdamW 主要可调参数及其影响说明 1. 学习率 lr 影响&#xff1a; 太大&#xff08;如 0.01 ~ 0.1&#xff09;&#xff1a;训练过程不稳&…

第一篇htmlcss详细讲解

第一章 HTML标签介绍 第一节 HTML基本结构 <!DOCTYPE html> <html><head><title>标题</title></head><body>文档主体</body></html> HTML 标签是由<>包围的关键词,例:<html> HTML 标签通常成对出现,分…

安达发|从救火到未雨绸缪:APS生产计划排产软件重塑制造业“危机免疫力“

在全球化竞争和市场需求多变的今天&#xff0c;制造企业面临着前所未有的挑战。订单波动、供应链中断、设备故障等突发情况已成为常态&#xff0c;许多企业陷入了"救火式管理"的恶性循环。据统计&#xff0c;超过70%的制造企业管理者将超过50%的工作时间用于处理各种…

短视频矩阵系统:选择与开发的全方位指南

短视频矩阵系统&#xff1a;选择与开发的全方位指南在当今数字化时代&#xff0c;短视频已经成为企业营销和个人品牌建设的重要工具。为了更高效地管理和发布短视频&#xff0c;许多企业和个人开始寻求短视频矩阵系统的解决方案。本文将深入探讨短视频矩阵系统哪家好、短视频批…