数据结构与算法篇-Prim最小生成树算法
实现方法1:基于边的优先级队列
“挑选最便宜边” 的最直接实现,就是将边本身存入优先队列。
核心思想
数据结构
inTree[v]:布尔数组,记录每个顶点是否已加入 MST。PQ:优先队列,存储边(u, v, weight),按权重升序排序。mstEdges:列表,存储选中的 MST 边。
策略:
- 从起始顶点出发,将其所有边加入优先队列。
- 提取权重最小的边。
- 若边的两个端点都已在树中,跳过(该边已失效)。
- 否则,将该边加入 MST,并将新顶点(非树端点)加入树中。
- 将新顶点的所有边加入优先队列。
- 重复步骤 2-5,直到收集到 ∣V∣−1|V|−1∣V∣−1 条边(MST 完成)。
伪代码
PRIM-EDGE-BASED(G, w, s):// Initializefor each vertex v in V:inTree[v] = falseinTree[s] = truemstEdges = []// Add all edges from starting vertex to PQfor each neighbor v of s with edge weight w(s, v):PQ.insert((s, v, w(s, v)))// Build the MSTwhile PQ is not empty and |mstEdges| < |V| - 1:// Get the minimum-weight edge(u, v, weight) = PQ.extractMin()// Skip if both endpoints already in tree (stale edge)if inTree[u] and inTree[v]:continue// Add edge to MSTmstEdges.add((u, v, weight))// Determine which endpoint is the new vertexnewVertex = inTree[u] ? v : uinTree[newVertex] = true// Add all edges from new vertex to non-tree verticesfor each neighbor w of newVertex with edge weight edgeWeight:if not inTree[w]:PQ.insert((newVertex, w, edgeWeight))return mstEdges
理解失效边
随着树的扩展,优先队列中部分边会变为 “失效边”:其两个端点已通过其他路径加入树中,不再具备扩展价值。
当提取到失效边时,直接检测并跳过即可。
这种处理完全合理:每条边仅被提取一次,丢弃失效边不会影响性能。
追踪示例
在之前的图中执行该算法,从顶点A开始:
╭───╮ ╭───╮│ A ├───── 4 ────┤ B │╰─┬─╯ ╰─┬─╯│ │2 3│ │╭─┴─╮ ╭─┴─╮│ C ├───── 5 ────┤ D │╰───╯ ╰───╯│ │6 1│ │╭─┴─╮ ╭─┴─╮│ E ├───── 7 ────┤ F │╰───╯ ╰───╯
数据结构
- inTree={}
- PQ=[]
- mstEdges=[]
初始化
- inTree={A}
- PQ=[(A,C,2),(A,B,4)]
- mstEdges=[]
迭代 1 :提取 (A,C,2)
inTree[A] = true, inTree[C]=false- 将(A,C,2)加到MST树中:mstEdges=[(A,C,2)]
- newVertex=C
- 将 C 添加到树:inTree={A,C}
- 遍历 C 的邻居A、D、E:
- inTree[A] = true,跳过
- inTree[D] = false,将(C,D,5)加入PQ,即PQ = [(A,B,4), (C,D,5)]
- inTree[E] = false,将(C,E,6)加入PQ,即PQ = [(A,B,4), (C,D,5),()C,E,6]
更新后的数据结构:
- inTree={A,C}
- PQ=[(A,B,4), (C,D,5),(C,E,6)]
- mstEdges=[(A,C,2)]
迭代 2: 提取 (A,B,4)
- inTree[A] = true, inTree[B]=false
- 将(A,B,4)加到MST树中:mstEdges=[(A,C,2),(A,B,4)]
- newVertex=B
- 将 B 添加到树:inTree={A,C,B}
- 遍历 B 的邻居A、D:
- inTree[A] = true,跳过
- inTree[D] = false,将(B,D,3)加入PQ,即PQ = [(B,D,3), (C,D,5),(C,E,6)]
更新后的数据结构:
- inTree={A,C,B}
- PQ=[(B,D,3), (C,D,5),(C,E,6)]
- mstEdges=[(A,C,2),(A,B,4)]
迭代 3: 提取 (B,D,3)。将 D 添加到树。PQ = [(D,F,1), (C,D,5), (C,E,6)]
- inTree[B] = true, inTree[D]=false
- 将(B,D,3)加到MST树中:mstEdges=[(A,C,2),(A,B,4),(B,D,3)]
- newVertex=D
- 将 D 添加到树:inTree={A,C,B,D}
- 遍历 D 的邻居B、C、F:
- inTree[B] = true,跳过
- inTree[C] = true,跳过
- inTree[F] = false,将(D,F,1)加入PQ,即PQ = [(D,F,1), (C,D,5),(C,E,6)]
更新后的数据结构:
- inTree={A,C,B,D}
- PQ=[(D,F,1), (C,D,5),(C,E,6)]
- mstEdges=[(A,C,2),(A,B,4),(B,D,3)]
迭代 4: 提取 (D,F,1)。将 F 添加到树。PQ = [(C,D,5), (C,E,6), (F,E,7)]
- inTree[D] = true, inTree[F]=false
- 将(D,F,1)加到MST树中:mstEdges=[(A,C,2),(A,B,4),(B,D,3),(D,F,1)]
- newVertex=F
- 将 F 添加到树:inTree={A,C,B,D,F}
- 遍历 F 的邻居D、E:
- inTree[D] = true,跳过
- inTree[E] = false,将(F,E,7)加入PQ,即PQ = [(C,D,5),(C,E,6),(F,E,7)]
更新后的数据结构:
- inTree={A,C,B,D,F}
- PQ= [(C,D,5),(C,E,6),(F,E,7)]
- mstEdges=[(A,C,2),(A,B,4),(B,D,3),(D,F,1)]
迭代 5: 提取 (C,D,5)。C 和 D 都在树中 — 过时,跳过!
- inTree[C] = true, inTree[D]=true:过时,跳过!
更新后的数据结构:
- inTree={A,C,B,D,F}
- PQ= [(C,E,6),(F,E,7)]
- mstEdges=[(A,C,2),(A,B,4),(B,D,3),(D,F,1)]
迭代 6: 提取 (C,E,6)。将 E 添加到树。完成 — 总共 5 条边。
- inTree[C] = true, inTree[E]=false
- 将(C,E,6)加到MST树中:mstEdges=[(A,C,2),(A,B,4),(B,D,3),(D,F,1), (C,E,6)]
- newVertex=E
- 将 E 添加到树:inTree={A,C,B,D,F,E}
- 遍历 E 的邻居C、F:
- inTree[C] = true,跳过
- inTree[F] = true,跳过
更新后的数据结构:
- inTree={A,C,B,D,F,E}
- PQ= [(F,E,7)]
- mstEdges=[(A,C,2),(A,B,4),(B,D,3),(D,F,1),(C,E,6)]
结果: A-C, A-B, B-D, D-F, C-E → 总权重:2+4+3+1+6=16
参考资料
- 两种方法实现Prim算法
- PrimEdgeBasedMst.java
- PrimEdgeBasedMstTest.java
