介绍
Prim 算法是求解最小生成树的经典算法之一。
它和 Kruskal 一样,都是基于贪心思想的最小生成树算法。
不同的是:
- Kruskal 是从边的角度出发,每次选择当前最小且不成环的边
- Prim 是从点的角度出发,每次选择距离当前生成树最近的点
堆优化 Prim 通常使用优先队列维护当前距离生成树最近的点,写法和 Dijkstra 很像,时间复杂度为 O(mlogn)。
知识点
- Prim 算法是一种基于顶点扩展的最小生成树贪心算法
- 堆优化 Prim 使用优先队列维护距离当前生成树最近的点
- 每次从堆中取出距离最小的未加入点
- 加入新点后,用它的所有邻边更新其他点到生成树的最小距离
- 如果最终没有加入所有点,说明原图不连通,不存在最小生成树
- 堆优化 Prim 的时间复杂度为 O(mlogn)
核心思想
Prim 算法的核心思想是:
当前已经有一棵生成树,每次选择一条连接"树内点"和"树外点"的最小边,把新的点加入生成树。
一开始,生成树中只有一个起点。
之后每次从所有可以连接到树外点的边中,选择权值最小的一条。
这条边连接的树外点会被加入生成树。
不断重复这个过程,直到所有点都被加入。
堆优化 Prim 中,我们用优先队列维护这些候选点,每次快速取出距离当前生成树最近的点。
算法步骤
-
建立无向图。
-
初始化
dist和vis数组。 -
从 1 号点开始,将
(0, 1)加入优先队列。 -
每次从优先队列中取出边权最小的点
u。 -
如果
u已经加入生成树,就跳过。 -
否则把
u加入生成树,并累加答案。 -
用
u的所有邻边更新其他点。
伪代码
function Prim_Heap(G, n):
dist[n+1] = INF
vis[n+1] = false
dist[1] = 0
priority_queue pq
pq.push((0, 1))
ans = 0
cnt = 0
while pq不为空 and cnt < n:
(d, u) = pq.top()
pq.pop()
if vis[u]: continue
vis[u] = true
ans += d
cnt++
for 每个邻接顶点v和边权w:
if not vis[v] and w < dist[v]:
dist[v] = w
pq.push((w, v))
if cnt == n: 返回ans
else: 返回"impossible"