背景介绍:
在上一篇 数据结构与算法分析-C++描述 第9章 图论算法(最小生成树之Prim算法)中介绍了最小生成树的Prim算法实现,该算法通过每次比较当前顶点与连接顶点的距离和上一顶点与连接顶点的距离寻找最小值的方式生成最小树。本篇将介绍实现最小生成树的另一贪心算法——Kruskal算法。
Kruskal算法语言描述:
Kruskal算法的贪心策略是连续地按照最小的权边选择,并且所选的边不产生回路时就把它作为选定的边。形式上,Kruskal算法在处理一个森林——树的集合。开始的时候是棵单节点树,添加一边则合并两棵树,当算法结束时只剩下一棵树,即最小生成树。
实现步骤:
1)按权重从大到小排列边;
2)从最小权重边的两个顶点开始建立树;
3)若加入的边不连通,则合并两棵子树;【注:连通的本质是寻找两个子树是否有同一父亲结点,在图中指是否有共同最大连通顶点,如v4 -> v7, v6 -> v7, 当v4和v6连接时,因其有共同父亲结点构成连通 】
4)从剩余最小边合并子树,重复3)过程;
Kruskal算法行为描述:
1,排序边并构建树,连接最小边为1的顶点;
2,连接最小边为2的顶点并判断是否连通,若不连通则合并两课子树;
3,连接最小边为3的顶点,判断是否连通,显然v2,v4连通,不合并该边;连接最小边为4的顶点,v1,v3连通,所以不合并该边,结果如图:
4,连接最小边为5的边,v3,v6连通,不合并该边,连接最小边为6的边:
5,依次进行,直到最小边为10时判断连通结束,最终结果如图:
当使用二叉堆时,Kruskal算法最坏情形时间运行时间为,由于,所有实际运行时间为。在实践中算法运行时间远快于该时间界。
一般情况,对于稠密的图通常使用Prim算法,因为一次比较可以更新一组距离权值;对于稀疏的图常用Kruskal算法,因为连通的情形更少,判断连通性将更快;
Kruskal算法编程实现:
//main.cpp
#include <iostream>
#include<fstream>
#include<algorithm>
using namespace std;
const int N = 20;
int parent[N];
struct Edge{
int u,v,w;
}edges[N];
//initialize the parent
void initParent(int numEdage){
for(int i = 1; i <= numEdage; i++){
parent[i] = -1;
}
}
//find the vertex's parent and squeeze the path
int findParent(int i){
int temp;
for(temp = i; parent[temp] >= 0; temp = parent[temp]);
while(temp != i){
int t = parent[i];
parent[i] = temp;
i = t;
}
return temp;
}
//merge the two vertex into a tree
void mergeVertex(int u,int v){
int r1 = findParent(u);
int r2 = findParent(v);
int tmp = parent[r1] + parent[r2];
if(parent[r1] > parent[r2]){
parent[r1] = r2;
parent[r2] = tmp;
}else{
parent[r2] = r1;
parent[r1] = tmp;
}
}
//kruskal algorithm
int kruskal(int numVertex, int numEdage){
int sum = 0;
int u,v;
initParent(numEdage);
for(int i = 0; i < numVertex; i++)
{
u = edges[i].u;
v = edges[i].v;
//judege whether the two vertex in same union
if(findParent(u) != findParent(v)){
cout << "Edage info : (" << u << ", " << v << ", " << edges[i].w << " )" << endl;
sum += edges[i].w;
mergeVertex(u, v);
}
}
return sum;
}
//the compar function
int largerThan(const void * a, const void * b){
//convert Edge* into const void Edge*
Edge * e1 = (Edge *)a;
Edge * e2 = (Edge *)b;
return e1->w - e2->w;
}
int main() {
int numEdage, numVertex;
//read data from file
ifstream fin("input.txt");
fin >> numEdage >> numVertex;
//update the edges
for(int i = 0; i < numVertex; i++){
fin >> edges[i].u >> edges[i].v >> edges[i].w;
}
//qsort parameters: void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));
qsort(edges, numVertex, sizeof(Edge), largerThan);
cout << "The MST is : " << kruskal( numVertex, numEdage) << endl;
return 0;
}
//input.txt
7 12
1 2 2
1 3 4
1 4 1
2 4 3
2 5 10
3 4 2
3 6 5
4 5 7
4 6 8
4 7 4
5 7 6
6 7 1
实验结果:
practice makes perfect !