什么是最小生成树 (MST)? 想象你要给好几个村庄通电,村庄之间连电线的费用不同。你的目标是:用最少的总费用 把所有村庄连通,且不形成回路。这个 “最省钱的连接方案” 就是最小生成树。
# 1. Kruskal 算法:加边法 (贪心 + 并查集)Kruskal 的思想非常直观:总是找最短的那条边 。
# 核心步骤:把图中所有的边按权值(长度)从小到大排序 。 依次取出边,如果这条边的两个端点还没连通,就选它;如果已经连通了,就丢弃(防止形成环)。 直到选够了 n − 1 n-1 n − 1 条边。 # C++ 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <vector> #include <algorithm> using namespace std;struct Edge { int u, v, w; bool operator <(const Edge& other) const { return w < other.w; } }; int parent[1005 ];int find (int i) { if (parent[i] == i) return i; return parent[i] = find (parent[i]); } int main () { int n, m; cin >> n >> m; vector<Edge> edges (m) ; for (int i = 0 ; i < m; i++) cin >> edges[i].u >> edges[i].v >> edges[i].w; sort (edges.begin (), edges.end ()); for (int i = 1 ; i <= n; i++) parent[i] = i; int mst_weight = 0 , count = 0 ; for (auto & edge : edges) { int rootU = find (edge.u); int rootV = find (edge.v); if (rootU != rootV) { parent[rootU] = rootV; mst_weight += edge.w; count++; } } if (count == n - 1 ) cout << "最小生成树权重: " << mst_weight << endl; else cout << "图不连通" << endl; return 0 ; }
# 2. Prim 算法:加点法 (切分定理)Prim 的思想是:从一个点出发,慢慢向外扩张 。
# 核心步骤:随意选一个起点加入 “已连接集合”。 寻找一条最短的边,这条边的一头在 “已连接集合” 里,另一头在 “未连接集合” 里。 把这个新点拉进来,重复步骤 2,直到所有点都在集合里。 # C++ 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <vector> #include <queue> using namespace std;const int INF = 1e9 ;typedef pair<int , int > PII; int main () { int n, m; cin >> n >> m; vector<vector<PII>> adj (n + 1 ); for (int i = 0 ; i < m; i++) { int u, v, w; cin >> u >> v >> w; adj[u].push_back ({w, v}); adj[v].push_back ({w, u}); } priority_queue<PII, vector<PII>, greater<PII>> pq; vector<int > dist (n + 1 , INF) ; vector<bool > visited (n + 1 , false ) ; int mst_weight = 0 ; dist[1 ] = 0 ; pq.push ({0 , 1 }); while (!pq.empty ()) { auto [d, u] = pq.top (); pq.pop (); if (visited[u]) continue ; visited[u] = true ; mst_weight += d; for (auto & edge : adj[u]) { int w = edge.first; int v = edge.second; if (!visited[v] && w < dist[v]) { dist[v] = w; pq.push ({dist[v], v}); } } } cout << "最小生成树权重: " << mst_weight << endl; return 0 ; }
# 💡 总结:我该选哪一个?算法 核心思想 适合场景 时间复杂度 Kruskal 找最短的边 稀疏图 (边比较少的情况)Prim 找最近的点 稠密图 (边非常多的情况)
面试小贴士: > 记住,Kruskal 依赖于排序和并查集 ,而 Prim 依赖于优先队列(堆) 。如果你是初学者,建议先熟练掌握 Kruskal,它的逻辑更符合直觉!