元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
算法基础
▶
复杂度分析
时间复杂度
空间复杂度
渐进符号
▶
算法思想
分治法
贪心算法
回溯法
概率算法
枚举算法
递推算法
递归算法
动态规划
分支限界法
模拟算法
▶
算法分析
正确性证明
性能优化
算法比较
▶
搜索算法
▶
基本搜索
顺序搜索
二分搜索
插值搜索
▶
图搜索
深度优先搜索
广度优先搜索
启发式搜索
▶
高级搜索
双向搜索
A*算法
模式匹配
▶
排序算法
▶
比较类排序
冒泡排序
快速排序
归并排序
插入排序
选择排序
▶
非比较类排序
计数排序
基数排序
桶排序
▶
高级排序
堆排序
希尔排序
外部排序
拓扑排序
▶
图论算法
▶
图的表示
邻接矩阵
邻接表
边集数组
▶
最短路径
Dijkstra算法
Floyd算法
Bellman-Ford算法
最短路径概述
▶
生成树
Prim算法
Kruskal算法
并查集
最小生成树
▶
图的连通性
强连通分量
割点与桥
双连通分量
欧拉路径
▶
动态规划
▶
基础概念
最优子结构
重叠子问题
状态转移方程
▶
经典问题
背包问题
最长公共子序列
编辑距离
▶
优化技巧
空间优化
状态压缩
区间动态规划
▶
字符串算法
▶
字符串匹配
KMP算法
Boyer-Moore算法
Sunday算法
▶
字符串处理
字典树
后缀数组
字符串哈希
▶
压缩算法
游程编码
Huffman编码
LZ77算法
发布时间:
2025-03-21 19:07
↑
☰
# 最小生成树算法 最小生成树(Minimum Spanning Tree,MST)是一个带权无向图所有生成树中权值和最小的生成树。本文将介绍两种经典的最小生成树算法:Prim算法和Kruskal算法。 ## Prim算法 ### 基本概念 Prim算法是一种用于寻找加权无向图的最小生成树的贪心算法。它的基本思想是从一个起始顶点开始,每次选择一个与当前树连接的权值最小的边,逐步扩展生成树。 ### 算法步骤 1. 初始化: - 选择一个起始顶点 - 创建一个集合存储已访问的顶点 - 创建一个优先队列存储边 2. 主循环: - 将当前顶点的所有邻边加入优先队列 - 选择权值最小且不会形成环的边 - 将新顶点加入已访问集合 - 重复直到所有顶点都被访问 ### 代码实现 ```python from heapq import heappush, heappop class PrimMST: def __init__(self, vertices): self.V = vertices self.graph = [[] for _ in range(vertices)] def add_edge(self, u, v, weight): """添加边""" self.graph[u].append((v, weight)) self.graph[v].append((u, weight)) def find_mst(self): """使用Prim算法找最小生成树""" # 初始化 start_vertex = 0 visited = [False] * self.V mst = [] total_weight = 0 # 优先队列存储边 (权重, 顶点, 父顶点) pq = [(0, start_vertex, -1)] while pq: weight, u, parent = heappop(pq) # 如果顶点已访问,跳过 if visited[u]: continue visited[u] = True # 将边加入MST if parent != -1: mst.append((parent, u, weight)) total_weight += weight # 将所有邻边加入优先队列 for v, w in self.graph[u]: if not visited[v]: heappush(pq, (w, v, u)) return mst, total_weight # 使用示例 mst = PrimMST(5) # 添加边 edges = [ (0, 1, 2), (0, 3, 6), (1, 2, 3), (1, 3, 8), (1, 4, 5), (2, 4, 7), (3, 4, 9) ] for u, v, w in edges: mst.add_edge(u, v, w) tree, total_weight = mst.find_mst() print("最小生成树的边:") for u, v, w in tree: print(f"{u} - {v}: {w}") print(f"总权重:{total_weight}") ``` ## Kruskal算法 ### 基本概念 Kruskal算法也是一种用于寻找加权无向图的最小生成树的贪心算法。它的基本思想是按照边的权值从小到大排序,每次选择一条不会形成环的最小权值边。 ### 算法步骤 1. 初始化: - 将所有边按权值排序 - 创建并查集用于检测环 2. 主循环: - 依次选择权值最小的边 - 使用并查集检查是否形成环 - 如果不形成环,将边加入生成树 ### 代码实现 ```python class DisjointSet: def __init__(self, vertices): self.parent = list(range(vertices)) self.rank = [0] * vertices def find(self, item): """查找集合的代表元素""" if self.parent[item] != item: self.parent[item] = self.find(self.parent[item]) return self.parent[item] def union(self, x, y): """合并两个集合""" px, py = self.find(x), self.find(y) if px == py: return if self.rank[px] < self.rank[py]: self.parent[px] = py elif self.rank[px] > self.rank[py]: self.parent[py] = px else: self.parent[py] = px self.rank[px] += 1 class KruskalMST: def __init__(self, vertices): self.V = vertices self.edges = [] def add_edge(self, u, v, weight): """添加边""" self.edges.append((u, v, weight)) def find_mst(self): """使用Kruskal算法找最小生成树""" # 按权重排序边 self.edges.sort(key=lambda x: x[2]) # 初始化并查集 ds = DisjointSet(self.V) mst = [] total_weight = 0 for u, v, weight in self.edges: # 如果加入这条边不会形成环 if ds.find(u) != ds.find(v): mst.append((u, v, weight)) total_weight += weight ds.union(u, v) return mst, total_weight # 使用示例 mst = KruskalMST(5) # 添加边 edges = [ (0, 1, 2), (0, 3, 6), (1, 2, 3), (1, 3, 8), (1, 4, 5), (2, 4, 7), (3, 4, 9) ] for u, v, w in edges: mst.add_edge(u, v, w) tree, total_weight = mst.find_mst() print("最小生成树的边:") for u, v, w in tree: print(f"{u} - {v}: {w}") print(f"总权重:{total_weight}") ``` ## 算法比较 ### 时间复杂度 1. Prim算法: - 使用二叉堆:O((V + E) log V) - 使用斐波那契堆:O(E + V log V) 2. Kruskal算法: - O(E log E) 或 O(E log V) ### 空间复杂度 1. Prim算法: - O(V) 2. Kruskal算法: - O(V) ### 适用场景 1. Prim算法: - 适用于稠密图 - 从单个顶点开始构建 - 适合实时构建最小生成树 2. Kruskal算法: - 适用于稀疏图 - 全局考虑边的权重 - 适合离线构建最小生成树 ## 实际应用 ### 网络设计 ```python class NetworkDesigner: def __init__(self, locations): self.V = locations self.connections = [] def add_connection(self, location1, location2, cost): """添加两个位置之间的连接成本""" self.connections.append((location1, location2, cost)) def design_network(self): """使用Kruskal算法设计最小成本网络""" mst = KruskalMST(self.V) for u, v, cost in self.connections: mst.add_edge(u, v, cost) network, total_cost = mst.find_mst() return network, total_cost # 使用示例 designer = NetworkDesigner(6) # 添加可能的连接和成本 connections = [ (0, 1, 10), # A-B: 10万 (0, 2, 15), # A-C: 15万 (1, 2, 5), # B-C: 5万 (1, 3, 12), # B-D: 12万 (2, 3, 8), # C-D: 8万 (2, 4, 20), # C-E: 20万 (3, 4, 7), # D-E: 7万 (3, 5, 9), # D-F: 9万 (4, 5, 6) # E-F: 6万 ] for src, dest, cost in connections: designer.add_connection(src, dest, cost) network, total_cost = designer.design_network() print("最优网络连接方案:") for u, v, cost in network: print(f"连接 {u} 到 {v},成本:{cost}万") print(f"总成本:{total_cost}万") ``` ### 电路设计 ```python class CircuitDesigner: def __init__(self, components): self.V = components self.connections = [] def add_wire(self, comp1, comp2, length): """添加两个组件之间的连线长度""" self.connections.append((comp1, comp2, length)) def optimize_circuit(self): """使用Prim算法优化电路布线""" mst = PrimMST(self.V) for u, v, length in self.connections: mst.add_edge(u, v, length) wiring, total_length = mst.find_mst() return wiring, total_length # 使用示例 designer = CircuitDesigner(5) # 添加可能的连线 wires = [ (0, 1, 2), # CPU-内存 (0, 2, 4), # CPU-硬盘 (1, 2, 1), # 内存-硬盘 (1, 3, 3), # 内存-电源 (2, 3, 5), # 硬盘-电源 (2, 4, 2), # 硬盘-网卡 (3, 4, 4) # 电源-网卡 ] for comp1, comp2, length in wires: designer.add_wire(comp1, comp2, length) wiring, total_length = designer.optimize_circuit() print("最优电路布线方案:") for u, v, length in wiring: print(f"连接组件 {u} 到 {v},线长:{length}cm") print(f"总线长:{total_length}cm") ``` ## 总结 1. 算法选择: - 对于稠密图,优先使用Prim算法 - 对于稀疏图,优先使用Kruskal算法 - 需要增量构建时,选择Prim算法 - 需要全局最优时,选择Kruskal算法 2. 实现考虑: - 使用优先队列优化Prim算法 - 使用并查集优化Kruskal算法 - 注意边的表示方式和数据结构选择 3. 应用场景: - 网络布线优化 - 电路设计 - 管道系统设计 - 交通网络规划