今天来写一下最小生成树的两种的算法,毕竟能把东西完整的表述出来才是真正的理解了。
prim算法的链接http://blog.csdn.net/winoros/article/details/21284783
kruskal算法思路为贪心
算法思路
1)将边按边权升序排序。
2)然后从最小的边开始选取加入图中,如果加入这条边后出现了环(也就是这条边的两个端点都已经有边与之相连了),那么这时就破坏了生成树的性质, 所以就不选择这条边。
3)这种选择一直进行到图中出现了一个生成树为止。
算法正确性的证明:
1)假设一颗最小生成树U,我们通过kruskal得到的树为V,两颗树中不同的边为u1, u2, u3, ……v1, v2, v3……(它们均按边权从小到大排序),两棵树中相同的边在边集T中。
2)我们把v1放入V中,明显,放入时都会在U中出现一个环,而且这个环中肯定会有u1, u2, u3, ……中的边,我们把其中最大的ui删去,得到一棵新的生成树P。
3)这时,我们把P和V做对比,明显,加入的v1取代了U中的某条边,如果v1小于删去的ui,那么P<U,这与U是最小生成树相矛盾。而如果v1大于删去的ui,那么ui应该是在v1之前考虑的,而v1又是在v2, v3,……之前考虑的,从而我们考虑有kruskal生成的树V,根据V的生成思路,V中之所以没有ui,是因为ui和T中的某些边构成了环。但是,ui和T又同时都在U中,从而也就是说U中出现了环,这与U的初始假设矛盾。所以ui和v1的权重是相同的。因此,新树P和假设的U的权重的是相同的。
4)现在对P和V重复1)~3)知道两者没有不同的边为止。由于P的权重一直都和所假设的最小生成树U的权重相等。所以,V和U的权重也是相等的。
综上,我们证明了树V确实是一颗最下生成树。
算法的具体实现中,判断加入一条边后是否会出现环可以使用并查集优化。
时间复杂度O(Elog2E),E为图的边数目,主要时间浪费在对边的排序上。所以相对prim算法,它更适合求稀疏图
下面贴出代码
//Kruskal 算法 //made by winoros struct edge { int from, int to, int cost; friend bool operator < (edge a, edge b) { return a.cost < b.cost } }e[]; //并查集内容 void ufs_init(int n) { for(int i = 0; i < n; i++) p[i] = i; } ind find(int i) { return f[i] == I ? i : f[i] = find(f[i]); } int merge(int i, int j) { f[find(i)] = find(j); } int p[];//并查集中点的父亲节点 //并查集结束 int kru(int n, int all) { //n 代表边数, all代表点的个数 int sum = 0, cnt = 0; sort(e, e + n); ufs_init(n); for(int i = 0; i < n; i++) { if(find(e[i].from) != find(e[i].to)) sum += e[i].cost, merge(e[i].from, e[i].to); if(++cnt == all - 1) break; } return cnt == all - 1 ? sum : -1;//这里返回的是最小生成树的总边权 }