最小生成树之克鲁斯卡尔(Kruskal)算法
克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用(所选的边不能构成回路)的最小权植边。所以Kruskal算法的第一步是给所有的边按照从小到大的顺序排序。这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每一条边(u,v)。
具体实现过程如下:
<1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。
<2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量 上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。
<3> 如此重复下去,直到所有顶点在同一连通分量上为止。
emmm排序取边这些很简单的,一个sort解决,但是,怎么判断两个点是不是在同一连通分量和怎么把边加到这个树上才是难点,
这里就可以并查集这个好东西了,至于什么是并查集,这里推荐大佬的博客:大佬博客
很有意思的
#include <iostream> #include <algorithm> using namespace std; const int maxn = 1e5 + 7; int bin[maxn]; int n, m; struct edge { int u, v, w; }edges[maxn]; void unit() { for (int i = 0; i < maxn; i++) bin[i] = i; } int Find(int x) { int px = x; while (px != bin[px]) px = bin[px]; return px; } void merge(int x, int y) { int fx = Find(x); int fy = Find(y); if (fx != fy) bin[fx] = fy; } bool cmp(edge a, edge b) //定义排序规则 { return a.w <= b.w; } void Kruskal() { int sumweight = 0; //生成树的权值 int num = 0; //已选用的边的数目 int u, v; //选用边的两个顶点 unit(); //初始化 parent[]数组 for (int i = 0; i < m; i++) { u = edges[i].u; v = edges[i].v; if (Find(u) != Find(v))//判断其是否在同一个连通分量 { printf("%d %d %d\n", u, v, edges[i].w); sumweight += edges[i].w; num++; merge(u, v); } if (num >= n - 1) break; } printf("weight of MST is %d\n", sumweight); } int main() { int u, v, w; //边的起点和终点及权值 cin >> n >> m;//读入顶点个数 n for (int i = 0; i < m; i++) { cin >> u >> v >> w;//读入边的起点和终点 edges[i].u = u; edges[i].v = v; edges[i].w = w; } sort(edges, edges + m, cmp);//排序 Kruskal(); return 0; } /* 测试数据: 输入: 6 10 1 2 6 1 3 1 1 4 5 2 3 5 2 5 3 3 5 6 3 6 4 3 4 5 4 6 2 5 6 6 输出: V1-V3=1 V3-V6=4 V6-V4=2 V3-V2=5 V2-V5=3 最小权值和=15 */