最小生成树基本概念

对于一个带权连通无向图 G=(V,E)G=(V,E),生成树是包含全部顶点、且没有回路的连通子图。

若图有 nn 个顶点,则任何生成树都恰好有 n1n-1 条边。生成树中所有边的权值之和称为该生成树的代价;在所有生成树中,代价最小的生成树称为最小生成树(MST)

任何带权连通无向图都至少存在一棵最小生成树。

最小生成树是否唯一

  • 若图中所有边的权值都不相同,则最小生成树唯一;
  • 若图中存在权值相同的边,最小生成树不一定不唯一。只有当同权边能够互相替换,并且不改变生成树的总代价时,才可能得到多棵最小生成树。

易错点: 边权互不相同是最小生成树唯一的充分条件,但存在同权边不代表最小生成树一定不唯一。

Prim 算法

基本思想

Prim 算法从一个顶点开始,不断向外扩展。设

T=(U,ET),T=(U,E_T),

其中:

  • UU:已经加入生成树的顶点集合;
  • ETE_T:已经选择的边集合。

初始时任取一个顶点 u0u_0

U={u0},ET=.U=\{u_0\},\qquad E_T=\varnothing.

之后,每次从满足 uUu\in UvVUv\in V-U 的所有边中,选择权值最小的边 (u,v)(u,v),然后更新:

U=U{v},ET=ET{(u,v)}.U=U\cup\{v\},\qquad E_T=E_T\cup\{(u,v)\}.

重复以上过程,直到 U=VU=V

选择规则

重点:从“树内”到“树外”的所有边中选择权值最小的边。

注意,不是只考察刚刚加入的顶点,而是考察当前整棵树中的所有顶点与外部顶点之间的边。

时间复杂度

书中 Prim 算法的时间复杂度为:

O(V2).O(|V|^2).

该实现的复杂度与边数 E|E| 无关,因此尤其适合稠密图

Kruskal 算法

基本思想

Prim 从某一个顶点开始不断扩张;Kruskal 不从固定顶点出发,而是按照边权从小到大依次选边。

初始时:

T=(V,).T=(V,\varnothing).

此时所有顶点都已存在,但没有边,每个顶点都是一个独立的连通分量。

选择规则

将所有边按权值从小到大排列。每遇到一条边 (u,v)(u,v),判断 uuvv 是否属于两个不同的连通分量:

  • 若属于不同的连通分量,则选择这条边;
  • 若已经属于同一个连通分量,则舍弃这条边,因为加入后会形成回路。

一直进行到选出 n1n-1 条边为止。

时间复杂度

书中指出,Kruskal 算法的主要开销是对所有边按照权值排序,因此时间复杂度为:

O(ElogE).O(|E|\log |E|).

其主要复杂度由边数 E|E| 决定,因此特别适合稀疏图

Prim 与 Kruskal 对比

对比 Prim Kruskal
出发方式 从一个顶点开始 从所有顶点开始
每次选择 树内到树外的最小边 当前权值最小且不成环的边
观察重点 顶点集合 UU
是否需要排序全部边 不需要 需要
防止成环方式 天然不会成环 判断两端是否已连通
时间复杂度 $O( V
更适合 稠密图 稀疏图

二者最核心的区别是:

  • Prim:从一个点开始,逐渐长成一棵树;
  • Kruskal:从很多小树开始,逐渐合并成一棵树。

也可以这样记:

Prim 看“当前树往外怎么长”。
Kruskal 看“下一条最便宜的边能不能加”。

总结

Prim

  • 任取一个顶点开始;
  • 每次选择树内到树外的最小边;
  • 共选择 n1n-1 条边;
  • 时间复杂度为 O(V2)O(|V|^2)
  • 适合稠密图。

Kruskal

  • 将所有边按权值从小到大排列;
  • 选择不会形成回路的边;
  • 共选择 n1n-1 条边;
  • 时间复杂度为 O(ElogE)O(|E|\log |E|)
  • 适合稀疏图。