Kruskal算法:
一、将图中所有边去掉,保留下所有点,变成了n个顶点的森林
二、贪婪准则:从所有边中依次选出权值最小的边,加入图中,还必须保证新加入的边不会产生环路,若产生环路,则抛弃
三、加到n-1条边,则结束循环,就会生成该图的最小生成树
伪代码如下:
现在的问题就是,如何判断是否有环路呢?
我们使用并查集的方法来判断,用二叉树实现并查集。查 即: 就是 find() 函数,对于每一个节点,使用find函数,则返回该节点所在二叉树的根节点的值。通过find 函数来判断 两个节点是否有同一个根节点,即是否在同一颗二叉树内。如果插入的边的两端节点都在同一颗树内,则插入该树,肯定会产生环路。反之,如果插入边的两个节点不在同一颗树内,则不会产生环路。
并 即: 就是 union_tree() 函数,先通过find 函数找到两个节点的根节点,如果不相同,则需要合并,合并就是把一个节点的根节点的值改成两一个节点的根节点,就是相当于,把一颗二叉树的根,连在了另一颗二叉树上,实现合并。
使用并查集解决了环路问题,那我们就用算法生成 最小树吧。
#include <iostream>
#include<algorithm>
using namespace std;
struct Edge {
int x, y;
int w;
}edge[100];
int fa[100]; //存放每个点所属的类父节点
int cmp(Edge &e1,Edge &e2) { //按权值从小到大排序,权值一样则按起始点排序
if (e1.w != e2.w) {
return e1.w < e2.w;
}
else {
return e1.x < e2.x;
}
}
int find(int x) { //寻找根节点
return fa[x] == x ? x : fa[x]=find(fa[x]);
}
void union_tree(int x, int y) { //将两个边集合并
int fx = fa[x];
int fy = fa[y];
if (fx != fy) {
fa[fx] = fy;
}
}
int main()
{
int n, m,x,y,w; //顶点个数和边的个数
int sum = 0;
int cnt = 0;//统计树的边
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &x, &y, &w);
edge[i].x = x;
edge[i].y = y;
edge[i].w = w;
}
sort(edge + 1, edge + m + 1,cmp);
for (int i = 1; i <= n; i++) { //初始化 根节点全部为自己,然后合并
fa[i] = i;
}
for (int i = 1; i <= m; i++) {
int x1 = find(edge[i].x);
int y1 = find(edge[i].y);
if (x1 != y1) {
sum += edge[i].w;
union_tree(x1, y1);
cnt++;
}
if (cnt == m - 1) {
break;
}
}
cout << sum << endl; //权值之和
return 0;
}
样例:
8
10
1 2 2
1 3 8
2 3 7
2 4 9
3 4 4
3 5 10
3 6 12
5 6 6
5 7 14
7 8 3
输出: