最小生成树Prim算法
718 字
4 分钟
最小生成树Prim算法

最小生成树 Prim 算法
Prim 算法用来求无向连通图的最小生成树。
最小生成树的意思是:选出一些边,把所有点连起来,并且总权重最小。
如果图不连通,就不存在最小生成树。
1、Prim 的核心思想
Prim 像是在“不断扩展一个集合”。
刚开始生成树里只有一个点。每次从连接“树内点”和“树外点”的边里,选权重最小的一条,把新的点加入生成树。
重复这个过程,直到所有点都加入。
2、和 Dijkstra 的相似点
Prim 和 Dijkstra 都会用到:
dist 数组st 数组小根堆但含义不同:
- Dijkstra 的
dist[j]是起点到j的最短路。 - Prim 的
dist[j]是点j连接到当前生成树的最小边权。
3、什么时候更新 dist
当一个点 p 加入生成树后,遍历它的所有邻边:
p -> j,边权 c如果 j 还没有加入生成树,并且 c < dist[j],就更新:
dist[j] = c;4、C++ 模板
#include <bits/stdc++.h>using namespace std;
typedef pair<int, int> PII;
const int N = 100010;const int M = 200010;const int INF = 0x3f3f3f3f;
int n, m;int h[N], e[M], w[M], ne[M], idx;int dista[N];bool st[N];
void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++;}
int prim() { memset(dista, 0x3f, sizeof dista);
priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, 1}); dista[1] = 0;
int cnt = 0; int ans = 0;
while (!heap.empty()) { auto t = heap.top(); heap.pop();
int distance = t.first; int ver = t.second;
if (st[ver]) continue;
st[ver] = true; cnt++; ans += distance;
for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; int c = w[i];
if (!st[j] && c < dista[j]) { dista[j] = c; heap.push({dista[j], j}); } } }
if (cnt < n) return -1; return ans;}
int main() { scanf("%d%d", &n, &m); memset(h, -1, sizeof h);
while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); add(b, a, c); }
printf("%d\n", prim()); return 0;}5、为什么无向边要加两次
如果题目给的是无向图:
a -- b存图时要写:
add(a, b, c);add(b, a, c);这样从 a 能走到 b,从 b 也能走到 a。
6、怎么判断图不连通
Prim 每次成功加入一个新点,就让 cnt++。
如果最后:
cnt < n说明不是所有点都能被连上,图不连通,没有最小生成树。
7、容易写错的地方
(1)边数组要开两倍
无向图每条边存两次,所以:
const int M = 200010;如果 m 最大是 100000,边数组至少要开 2 * m。
(2)Prim 的 dist 不是起点最短路
它表示“连接到当前生成树的最小边权”。
(3)加入生成树时再累加答案
ans += distance;这个点真正被选中时,才把连接它的边权加入答案。
总结
Prim 的关键词是:
从树外选一条最小边,把新点加入生成树它和 Dijkstra 写法很像,但 dist 的意义不同。理解这一点,模板就不容易混。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














