算法 | Kruskal 算法
求最小生成树(适合稀疏图):Kruskal (克鲁斯卡尔) 算法
- 最小生成树是一副连通加权无向图中一棵权值最小的生成树。
Kruskal 算法
时间复杂度
时间复杂度:$O(\left|E\right|\log\left|V\right|)$
$\left|E\right|$ 为边数,$\left|V\right|$ 为顶点数,该版本适合稀疏图,即 $\left|E\right|\ll\left|V\right|^2$ 的图。
代码实现
const int MAXN = 1e5 + 10, INF = 0x3f3f3f3f;
int v, e; // v顶点数 e边数
int fa[MAXN]; // 并查集
struct Edge // 边的结构体,重载了<供sort()
{
int start, end, dist;
bool operator<(const Edge &x) const
{
return dist < x.dist;
}
};
vector<Edge> edges; // 邻接表存边
inline void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
inline void merge(int i, int j)
{
fa[find(i)] = find(j);
}
int kruskal()
{
sort(edges.begin(), edges.end());
int ans = 0, selected = 0;
bool flag = false;
for (auto ed : edges)
{
if (find(ed.start) != find(ed.end))
{
merge(ed.start, ed.end);
ans += ed.dist;
if (++selected == v - 1)
{
flag = true;
break;
}
}
}
return flag ? ans : INF;
}
分析
该算法的核心思想就是:选择 $A$ 的边集合外,权值最小且不会成环的边,加入到 $A$ 中。
实现“选择 $A$ 的边集合外”的方法:将边排序,从小到大有序选择。
实现“权值最小”的方法:将边储存在 vector
内,按长度排序(我用的重载运算符和 sort()
)
实现“不会成环”的方法:判断两节点是否已经联通,若已经联通,再加一条边肯定会成环,只有不连通才能合并(我用的并查集)。
下面用例子模拟一下该算法的过程:
初始化并查集:
将每个节点指向自身,图中用不同颜色表示。
找到最短的边 $1\leftrightarrow 3$,长度为 $1$:
$fa[1]\neq fa[3]$,不会成环。将 $1$、$3$ 节点合并。
找到最短的边 $4\leftrightarrow 6$,长度为 $2$:
$fa[4]\neq fa[6]$,不会成环。将 $4$、$6$ 节点合并。
找到最短的边 $2\leftrightarrow 5$,长度为 $3$:
$fa[2]\neq fa[5]$,不会成环。将 $2$、$5$ 节点合并。
找到最短的边 $3\leftrightarrow 6$,长度为 $4$:
$fa[3]\neq fa[6]$,不会成环。将 $3$、$6$ 节点合并。
找到最短的边 $1\leftrightarrow 4, 3\leftrightarrow 4, 2\leftrightarrow 3$,长度为 $5$:
$fa[1]=fa[4], fa[3]=fa[4]$,会成环。不可合并。
$fa[2]\neq fa[3]$,不会成环。将 $2$、$3$ 节点合并。
这时已经求出了最小生成树,后续操作均会成环,因此都会被跳过。
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi