【算法模板】最小生成树:稠密图选 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
相关推荐
王老师青少年编程3 小时前
2025年【江苏“信息与未来”编程思维】真题及题解(T2:坐标变换)
c++·题解·真题·坐标变换·编程思维·江苏·信息与未来
CS创新实验室4 小时前
算法、齿轮与硅基大脑:数值计算发展简史
人工智能·算法·数值计算
似是燕归来5 小时前
嵌入式工程师进阶指南:适配器编程设计模式一
c语言·stm32·适配器模式·嵌入式开发
skywalk81635 小时前
设计并实现段言的 C FFI 绑定机制 @Trae
c语言·开发语言·python·编程
海石6 小时前
1563分的简单题,可能就简单在能被暴力AC
算法·leetcode
海石6 小时前
1400分的dp汗流浃背之【交替子数组计数】
算法·leetcode
奋发向前wcx6 小时前
P2590 树的统计 题目解析
数据结构·算法·深度优先
2023自学中6 小时前
C++ 内存追踪器
linux·c++
imbackneverdie7 小时前
AI4S不止于分子药物:以MedPeer为代表的科研基建打开产业新增量
大数据·人工智能·算法·aigc·科研·学术·ai 4s
额鹅恶饿呃8 小时前
C语言中的数据结构和变量
c语言·数据结构·算法