【算法模板】最小生成树:稠密图选 Prim,稀疏图选 Kruskal

最小生成树:在连通带权无向图中,找到一个边权和最小的生成树

Prim 算法 -不断加点

核心思想:从一个顶点开始,每次将距离当前生成树最近的顶点加入树中。

算法步骤

  1. 初始化:任选一个顶点作为起点,初始化所有顶点到生成树的距离为无穷大 (INF),起点距离为 0。
  2. 循环 n 次:
    · 找最近点:在未加入生成树的顶点中,找到距离当前生成树最近的点 t。
    · 判断连通性:如果 distt == INF,说明图不连通,不存在 MST。
    · 加入生成树:将点 t 标记为已加入,并将 distt 累加到总权值中。
    · 更新距离:用点 t 的所有邻边,更新其他未加入顶点到生成树的距离。

代码关键点:

· 数据结构:使用邻接矩阵 edges\[\]\[\] 存储图。

· 距离数组:distj 表示顶点 j 到当前整个生成树(而不仅是某个点)的最小距离。

· 重边处理:建图时使用 min(edgesxy, z) 保留最小权值。

代码

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

const int N = 5010, INF = 0x3f3f3f3f;
int n, m;
int edges[N][N];  // 邻接矩阵
int dist[N];      // 到生成树的最短距离
bool st[N];       // 是否已加入生成树

int prim() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    int ret = 0;
    
    for (int i = 1; i <= n; i++) {
        int t = 0;  // 初始化为0,dist[0]为无穷大
        
        // 1. 找距离生成树最近的点
        for (int j = 1; j <= n; j++) {
            if (!st[j] && dist[j] < dist[t]) {
                t = j;
            }
        }
        
        // 判断是否连通
        if (dist[t] == INF) return INF;
        
        st[t] = true;
        ret += dist[t];
        
        // 2. 更新其他点到生成树的距离
        for (int j = 1; j <= n; j++) {
            if (!st[j]) {
                dist[j] = min(dist[j], edges[t][j]);
            }
        }
    }
    return ret;
}

int main() {
    cin >> n >> m;
    memset(edges, 0x3f, sizeof edges);
    
    for (int i = 1; i <= m; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        // 处理重边,保留最小权值
        edges[x][y] = edges[y][x] = min(edges[x][y], z);
    }
    
    int ret = prim();
    if (ret == INF) cout << "orz" << endl;
    else cout << ret << endl;
    
    return 0;
}

注意:

  • 使用邻接矩阵,是和稠密图
  • distj表示顶点j到当前整个生成树的最小距离
  • 时间复杂度O(n * n)

Kruskal 算法 - 不断加边

核心思想:按边权从小到大排序,依次选择不会形成环的边

算法步骤:

  1. 将所有边按权值从小到大排序
  2. 初始化\[查并集],每一个顶点自成集合
  3. 遍历排序后的边
    • 如果边的两个顶点不在集合,则选择该边
    • 合并这两个顶点所在的集合
  4. 当选中n-1条边时结束

代码

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 5010, M = 2e5 + 10, INF = 0x3f3f3f3f;

struct Edge {
    int x, y, z;
} edges[M];

int n, m;
int fa[N];  // 并查集

bool cmp(Edge& a, Edge& b) {
    return a.z < b.z;
}

int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int kruskal() {
    // 1. 边按权值排序
    sort(edges + 1, edges + 1 + m, cmp);
    
    // 2. 初始化并查集
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    
    int cnt = 0;  // 已选边数
    int ret = 0;  // 总权值
    
    // 3. 遍历所有边
    for (int i = 1; i <= m; i++) {
        int x = edges[i].x, y = edges[i].y, z = edges[i].z;
        int fx = find(x), fy = find(y);
        
        if (fx != fy) {  // 不连通,可以加入
            fa[fx] = fy;
            ret += z;
            cnt++;
            if (cnt == n - 1) break;  // 已选够边
        }
    }
    
    return cnt == n - 1 ? ret : INF;
}

int main() {
    cin >> n >> m;
    
    for (int i = 1; i <= m; i++) {
        cin >> edges[i].x >> edges[i].y >> edges[i].z;
    }
    
    int ret = kruskal();
    if (ret == INF) cout << "orz" << endl;
    else cout << ret << endl;
    
    return 0;
}

注意

  • 使用边集数组+并查集,适合稀疏图
  • 需要判断最终是否选中n-1条边来判断联通性
  • 时间复杂度:O(m log m)

算法对比

特性 prim 算法 Kruskal 算法
思想 加点法 加边法
数据结构 邻接矩阵 边集数组+查并集
时间复杂度 O(n * n) O(m log m)
适用场景 稠密图(m~n * n) 稀疏图(m <<n* n)
连通性判断 过程中判断 distt==INF 最后判段边数cnt==n-1
  • 边数多,用Prim
  • 边数少,用Kruskal
相关推荐
Jack205 小时前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树7 小时前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术1 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
clint4561 天前
C++进阶(1)——前景提要
c++
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
夜悊1 天前
C++代码示例:进制数简单生成工具
c++