以下介绍最小生成树的几个算法,Kruskal算法、Prim算法。
虽然网上有很多对于最小生成树的优质讲解,但是自己还是想写一下自己的理解与体会。
此处借用一下已有的经验(图解深深的吸引了我)。
Kruskal
先来讲一下kruskal算法,该算法的想法很简单,始终从未选取的边中选取最短的边并添加进去就好了。(此处未选取的边是指那些,边的任意一个顶点都没有被添加过),如有不明白的地方,请结合上面链接中的图解,进行推演并理解。
以下是模板:
/*
*用边构图,也就是下面的Side结构体
*/
struct Side{
int from, to, wei;
bool operator < (const struct Side s)const {
return wei < s.wei;
}
}side[MAXE];
int kruskal(int n, int m)
{
int Min = 0, node = n - 1;
int root[MAXN];
for(int i = 0; i <= n; ++i) root[i] = i;
sort(side, side + sideNum);
for(int i = 0; node && i < sideNum; ++i) {
if(Union(side[i], root)) {
node--;
Min += side[i].wei;
}
}
return Min;
}
bool Union(const struct Side &s, int *root)
{
int p = Find(s.from, root);
int q = Find(s.to, root);
if(p != q) {
root[p] = q;
return true;
}
return false;
}
int Find(int p, int *root)
{
while(p != root[p]) {
root[p] = root[root[p]];
p = root[p];
}
return p;
}
以上算法的贪心思想不难理解,然而对于贪心思想的正确性,此处运用了UnionFind数据结构。
正是UnionFind保证了选取边的时候,只考虑蓝边而忽略红边,从而可以得到生成树。
prim
prim算法与Dijstra算法相当类似,只是将原来的dist改为cost,用cost代表与该点相连的所有边中的最小权值。同样可以参照上面链接,进行推演理解。
以下是模板:
/*
*此处采用的是邻接矩阵存储边的权值
*/
int prim(int N) {
int res = 0;
for(int i = 0; i < N; ++i)
cost[i] = INF, vis[i] = false;
cost[0] = 0;
while(true) {
int u = -1;
for(int v = 0; v < N; ++v) {
if(!vis[v] && (u == -1 || cost[v] < cost[u])) u = v;
}
if(u == -1) break;
vis[u] = true;
res += cost[u];
for(int v = 0; v < N; ++v) {
cost[v] = min(cost[v], Map[u][v]);
}
}
return res;
}