最小生成树Kruskal算法:
先来说一下Kruskal算法的步骤:
1.建图,存储图中每条边的信息(start,end,cost:起点,终点,权值)
2.将图中各边按照权值由小到大排列
3.选取当前权值最小的边,如果该边的start,end,分别在两个不同的联通分量中,则加入该边
4.重复3. 直到所有点在同一个连通分量中
由步骤可以看出Kruskal算法的时间消耗主要用在排序上,所以在稀疏图中Kruskal算法有一定的优势
在这个算法中还用到并查集的知识,用于判断start,end是否在同一连通分量中
所以先来
并查集:
int Pre[MAX],Rank[MAX]; //Pre用来存储每个点的父亲,Rank则对应树的高度
void Init(){
for(int i=0;i<=N;i++){//初始化
Pre[i]=i;
Rank[i]=1;
}
}
int Find(int x){ //路径压缩,寻找父节点
return Pre[x]==x ? x: Pre[x]=Find(Pre[x]);
}
void Unite(int a,int b){
int x=Find(a);
int y=Find(b);
if(x!=y){ //如果a,b两点的父亲节点不同,合并
if(Rank[x]>Rank[y]){
Pre[y]=x;
Rank[x]+=Rank[y];
}
else {
Pre[x]=y;
Rank[y]+=Rank[x];
}
}
}
int Same(int a,int b){//判断a,b是否属于同一父节点
return Find(a)==Find(b);
}
Kruskal算法:
struct edge{ //定义边的结构体
int start,end;
double cost;
};
edge Edge[MAX]; //MAX表示最大变的数量
bool comp(const edge &a, const edge &b){ //自定义排序函数 ,按cost值由小到大排列
return a.cost<b.cost;
}
int Pre[MAX],Rank[MAX]; //Pre用来存储每个点的父亲,Rank则对应树的高度
double Kruskal(){
sort(Edge,Edge+cont,comp); //排序
Init(); //并查集初始化
double sum=0; //最小生成树长度
for(int i=0;i<cont;i++){ //按cost从小到大选取
edge tempE=Edge[i];
if(!Same(tempE.start,tempE.end)){ //该边的起始点,跟终点不在同一连通分量
sum+=tempE.cost; //sum+cost
Unite(tempE.start,tempE.end);//合并
}
}
return sum;
}
如有异议欢迎指正,谢谢