元素码农
基础
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:23
↑
☰
# Prim算法 Prim算法是一种用于寻找加权无向图的最小生成树的贪心算法。本文将详细介绍Prim算法的原理、实现和应用。 ## 基本概念 ### 最小生成树 最小生成树(Minimum Spanning Tree,MST)是一个连通无向图的一个子图,它包含图中的所有顶点,并且是一棵权值和最小的树。 ### 算法适用条件 1. 无向图 2. 边权重为非负数 3. 图必须是连通的 4. 适用于稠密图 ## 算法原理 ### 核心思想 Prim算法使用贪心策略,从一个起始顶点开始,每次选择一个与当前生成树相连的最小权重边,逐步扩展生成树直到包含所有顶点。 ### 算法步骤 1. 初始化: - 选择一个起始顶点加入生成树 - 其他顶点的距离设为无穷大 2. 主循环: - 在未访问的顶点中选择距离最小的顶点u - 将u加入生成树 - 更新u的邻接顶点的距离 3. 重复步骤2直到所有顶点都加入生成树 ## 代码实现 ### 基本实现 ```go type Edge struct { to int weight int } type Graph struct { vertices int adj [][]Edge } func NewGraph(v int) *Graph { return &Graph{ vertices: v, adj: make([][]Edge, v), } } func (g *Graph) AddEdge(from, to, weight int) { // 无向图需要添加两条边 g.adj[from] = append(g.adj[from], Edge{to: to, weight: weight}) g.adj[to] = append(g.adj[to], Edge{to: from, weight: weight}) } func (g *Graph) Prim() ([]Edge, int) { // 初始化数据结构 dist := make([]int, g.vertices) parent := make([]int, g.vertices) visited := make([]bool, g.vertices) // 初始化距离为无穷大 for i := range dist { dist[i] = math.MaxInt32 parent[i] = -1 } // 选择顶点0作为起始点 dist[0] = 0 // 主循环 mst := make([]Edge, 0, g.vertices-1) totalWeight := 0 for i := 0; i < g.vertices; i++ { // 找到未访问顶点中距离最小的 u := -1 minDist := math.MaxInt32 for v := 0; v < g.vertices; v++ { if !visited[v] && dist[v] < minDist { u = v minDist = dist[v] } } if u == -1 { break // 图不连通 } // 将顶点加入生成树 visited[u] = true if parent[u] != -1 { mst = append(mst, Edge{to: u, weight: dist[u]}) totalWeight += dist[u] } // 更新邻接顶点的距离 for _, edge := range g.adj[u] { v := edge.to if !visited[v] && edge.weight < dist[v] { dist[v] = edge.weight parent[v] = u } } } return mst, totalWeight } ``` ### 优先队列优化 ```go type Item struct { vertex int distance int index int } type PriorityQueue []*Item // 实现heap.Interface所需的方法 func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].distance < pq[j].distance } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] pq[i].index = i pq[j].index = j } func (pq *PriorityQueue) Push(x interface{}) { n := len(*pq) item := x.(*Item) item.index = n *pq = append(*pq, item) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] old[n-1] = nil item.index = -1 *pq = old[0 : n-1] return item } func (g *Graph) PrimWithPQ() ([]Edge, int) { // 初始化数据结构 dist := make([]int, g.vertices) parent := make([]int, g.vertices) visited := make([]bool, g.vertices) for i := range dist { dist[i] = math.MaxInt32 parent[i] = -1 } // 初始化优先队列 pq := make(PriorityQueue, 0) heap.Init(&pq) // 选择顶点0作为起始点 dist[0] = 0 heap.Push(&pq, &Item{vertex: 0, distance: 0}) mst := make([]Edge, 0, g.vertices-1) totalWeight := 0 for pq.Len() > 0 { // 取出最小距离顶点 u := heap.Pop(&pq).(*Item) if visited[u.vertex] { continue } // 将顶点加入生成树 visited[u.vertex] = true if parent[u.vertex] != -1 { mst = append(mst, Edge{to: u.vertex, weight: dist[u.vertex]}) totalWeight += dist[u.vertex] } // 更新邻接顶点的距离 for _, edge := range g.adj[u.vertex] { v := edge.to if !visited[v] && edge.weight < dist[v] { dist[v] = edge.weight parent[v] = u.vertex heap.Push(&pq, &Item{vertex: v, distance: edge.weight}) } } } return mst, totalWeight } ``` ## 应用示例 ### 网络布线 ```go type Location struct { id int name string x, y float64 } type NetworkPlanner struct { locations []Location graph *Graph } func NewNetworkPlanner(locations []Location) *NetworkPlanner { n := len(locations) planner := &NetworkPlanner{ locations: locations, graph: NewGraph(n), } // 构建完全图 for i := 0; i < n; i++ { for j := i + 1; j < n; j++ { // 计算两点间距离作为权重 dx := locations[i].x - locations[j].x dy := locations[i].y - locations[j].y weight := int(math.Sqrt(dx*dx + dy*dy)) planner.graph.AddEdge(i, j, weight) } } return planner } func (np *NetworkPlanner) GetOptimalLayout() ([]struct { from, to string cost int }, int) { // 使用Prim算法获取最小生成树 edges, totalCost := np.graph.PrimWithPQ() // 转换结果为可读格式 layout := make([]struct { from, to string cost int }, len(edges)) for i, edge := range edges { layout[i] = struct { from, to string cost int }{ from: np.locations[edge.to].name, to: np.locations[parent[edge.to]].name, cost: edge.weight, } } return layout, totalCost } ``` ### 电路设计 ```go type Component struct { id int name string position struct{ x, y float64 } voltage float64 } type CircuitDesigner struct { components []Component graph *Graph } func (cd *CircuitDesigner) AddComponent(comp Component) { cd.components = append(cd.components, comp) // 更新图结构 n := len(cd.components) if cd.graph == nil { cd.graph = NewGraph(n) } else { // 扩展图 newGraph := NewGraph(n) // 复制原有边 for i := 0; i < n-1; i++ { newGraph.adj[i] = append(newGraph.adj[i], cd.graph.adj[i]...) } cd.graph = newGraph } // 添加新组件与其他组件的连接 for i := 0; i < n-1; i++ { weight := cd.calculateConnectionCost(cd.components[i], comp) cd.graph.AddEdge(i, n-1, weight) } } func (cd *CircuitDesigner) GetOptimalWiring() ([]struct { from, to string length float64 }, float64) { edges, totalLength := cd.graph.PrimWithPQ() wiring := make([]struct { from, to string length float64 }, len(edges)) for i, edge := range edges { wiring[i] = struct { from, to string length float64 }{ from: cd.components[edge.to].name, to: cd.components[parent[edge.to]].name, length: float64(edge.weight) / 100, // 转换为实际长度 } } return wiring, float64(totalLength) / 100 } ``` ## 性能分析 ### 时间复杂度 1. 基本实现: - 查找最小距离顶点:O(V) - 更新距离:O(E) - 总体复杂度:O(V² + E) = O(V²) 2. 优先队列优化: - 出队操作:O(log V) - 更新距离:O(E log V) - 总体复杂度:O(E log V) ### 空间复杂度 1. 基本数据结构: - 邻接表:O(V + E) - 辅助数组:O(V) - 总体复杂度:O(V + E) 2. 优先队列实现: - 邻接表:O(V + E) - 优先队列:O(V) - 总体复杂度:O(V + E) ### 优化建议 1. 数据结构选择: - 稠密图使用邻接矩阵 - 稀疏图使用邻接表 - 考虑使用斐波那契堆 2. 实现优化: - 使用并查集检测连通性 - 预处理边权重 - 批量更新优化 ## 总结 1. 算法特点: - 贪心策略 - 适用于无向图 - 生成最小生成树 2. 实现考虑: - 根据图的特性选择实现方式 - 注意数据结构的选择 - 考虑实际应用场景的需求 3. 应用领域: - 网络布线 - 电路设计 - 管道铺设 - 交通规划