首页/数据结构/05-graph/最小生成树 🔗 在 Obsidian 中打开
数据结构 · 05-graph

最小生成树

难度 ★★★重要度 ★★★★★ 考查频率 高题型 选择 / 综合应用 数据结构/图最小生成树PrimKruskal
速查
生成树 = 含全部 $n$ 个顶点、$n-1$ 条边的极小连通子图;MST 边权和最小。Prim 加点法 $O(|V|^2)$(稠密图),Kruskal 加边法 $O(|E|\log|E|)$(稀疏图)。

速查

项目
主题最小生成树(MST)
核心概念连通图的极小连通子图,含所有顶点,仅 $n-1$ 条边,且边权之和最小
时间复杂度$O(|E|)$、$O(|E|\log|E|)$、$O(|V|^2)$
难度⭐⭐⭐
重要性⭐⭐⭐⭐⭐

核心概念

一、定义与性质

  • 生成树:连通图的极小连通子图,包含图中所有顶点,只有 $n-1$ 条边
  • 最小生成树(MST):边权之和最小的生成树
  • 最小生成树可能不唯一;若边权互不相同则唯一
  • $n$ 个顶点的 MST 恰有 $n-1$ 条边

二、Prim 算法(加点法)

从某个顶点开始,每次选择与当前树相连的最小权边,将新顶点加入树中。

void Prim(MGraph G, int v0) {
    int lowcost[MaxVertexNum];  // 到生成树的最小距离
    int adjvex[MaxVertexNum];   // 最小边的另一端
    for (int i = 0; i < G.vexnum; i++) {
        lowcost[i] = G.edge[v0][i];
        adjvex[i] = v0;
    }
    lowcost[v0] = 0;
    for (int i = 1; i < G.vexnum; i++) {
        int min = INF, k = -1;
        for (int j = 0; j < G.vexnum; j++)
            if (lowcost[j] != 0 && lowcost[j] < min) { min = lowcost[j]; k = j; }
        printf("(%d, %d)\n", adjvex[k], k);
        lowcost[k] = 0;
        for (int j = 0; j < G.vexnum; j++)
            if (G.edge[k][j] < lowcost[j]) { lowcost[j] = G.edge[k][j]; adjvex[j] = k; }
    }
}

时间复杂度 $O(|V|^2)$,适合稠密图

三、Kruskal 算法(加边法)

每次选择权值最小且不构成环的边加入生成树。

typedef struct { int u, v, w; } Edge;

void Kruskal(Edge edges[], int n, int e) {
    Sort(edges, e);                // 按权值排序
    int UFSets[MaxSize]; Init(UFSets, n);
    int count = 0;
    for (int i = 0; i < e && count < n-1; i++) {
        int u = edges[i].u, v = edges[i].v;
        if (Find(UFSets, u) != Find(UFSets, v)) {
            printf("(%d, %d)\n", u, v);
            Union(UFSets, u, v);
            count++;
        }
    }
}

时间复杂度 $O(|E|\log|E|)$(主要取决于排序),适合稀疏图

四、Prim vs Kruskal

特性PrimKruskal
策略从顶点扩展从边选择
时间$O(|V|^2)$$O(|E|\log|E|)$
适用稠密图稀疏图
实现类似 Dijkstra需要并查集

手算示例

651 324 65 A B C D E
带权图 G(顶点 A~E)

例 1:Prim(从 A 开始)

步骤选边lowcost 更新
初始化$[0, 6, 5, 1, \infty]$
1(A,D)=1$[0, 6, 4, 0, 5]$
2(D,C)=4$[0, 3, 0, 0, 5]$
3(C,B)=3$[0, 0, 0, 0, 2]$
4(B,E)=2$[0, 0, 0, 0, 0]$

MST 边:(A,D)=1, (D,C)=4, (C,B)=3, (B,E)=2,总权值 10

例 2:Kruskal

边按权排序:(A,D)=1, (B,E)=2, (B,C)=3, (C,D)=4, (A,C)=5, (D,E)=5, (A,B)=6, (C,E)=6。

依次选 (A,D)、(B,E)、(B,C)、(C,D),已 4 条边($n=5$)停止。总权值 10

常见考法

考法 1:Prim 手算给出图,用 Prim 算法逐步求 MST。
考法 2:Kruskal 手算给出图,用 Kruskal 算法逐步选边并判断是否成环。
考法 3:时间复杂度Prim $O(|V|^2)$,Kruskal $O(|E|\log|E|)$。
考法 4:适用场景稠密图用 Prim,稀疏图用 Kruskal。

易错点

注意
  1. Prim 每次选一个顶点;Kruskal 每次选一条边
  2. Kruskal 需要判断环(用并查集)
  3. MST 不唯一:边权相同时可能有多个 MST
  4. $n$ 个顶点 MST 恰有 $n-1$ 条边
  5. Prim 的 lowcost 表示到已选顶点集的最短距离

核心结论

算法时间复杂度空间复杂度适用场景
Prim$O(|V|^2)$$O(|V|)$稠密图
Kruskal$O(|E|\log|E|)$$O(|E|)$稀疏图

记忆卡片

Prim 算法的核心思想?
从顶点开始,每次选与当前树相连的最小权边。
Kruskal 算法的核心思想?
每次选最小权且不构成环的边(用并查集判断)。
n 个顶点的 MST 有几条边?
$n-1$ 条。
稠密图用哪个 MST 算法?
Prim($O(|V|^2)$)。
Kruskal 用什么判断是否成环?
并查集(Union-Find)。
MST 是否唯一?
不一定;边权互异时唯一。

交互动画 · Prim vs Kruskal

1 6 5 3 2 4 6 5 A B C D E
选择算法后点击「播放」
Prim 加点 / Kruskal 加边,目标都是总权值最小

相关知识点

graph-traversal shortest-path topological-sort critical-path mst-and-shortest-path

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。