概念:
Kruskal:每次选择图中边权值最小的边加入最小生成树,并且加入的边不能构成回路.
判断加入的边是否会构成回路 ==> 采用并查集方法
并查集(UnionFindSet):是一种树型的数据结构,用于处理一些不想交集合的合并及查询问题
主要操作:
(1)初始化:把每个点所在集合初始化为其自身
(2)查找:查找两个元素所在的集合,即找祖宗
注意:查找时,采用递归的方法找其祖宗,祖宗集合号等于自己时即停止。在回归时,把当前结点到祖宗路径上的所有结点统一为祖宗的集合号。
(3)合并:如果两个元素的集合号不同,将两个元素合并为一个集合。
注意:合并时只需要把一个元素的祖宗集合号,改为另一个元素集合号。擒贼先擒王,只改祖宗即可。
Code:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct T{
int from,to,w;//存储边及边权值
};
vector<T>Edge;
vector<int>P;//记录每个节点的父亲
vector<T>Path;//记录最小生成树的边
int N,M,ans=0;
bool cmp(T e1,T e2){//将从小到大排列
return e1.w<e2.w;
}
int find(int x){//查找 查找x的父亲
return P[x]==x?x:P[x]=find(P[x]);//递归思想 一直查到p[x]==x为止 此时的x就是其父亲结点
}
bool unite(int x,int y){
x=find(x);//查找x的父亲结点
y=find(y);//查找y的节点
if(x!=y){//x和y不是同一个父亲 将其合并
P[x]=y;
return true;
}
return false;
}
void kruskal(){
sort(Edge.begin(),Edge.end(),cmp);//对边的权值进行排序 kruskal每次都是选择最小边进行添加
for(int i=0;i<M;i++){
int x=Edge[i].from,y=Edge[i].to,w=Edge[i].w;
if(unite(x,y)){//如果可以合并 说明xy边是最小边可以加到最小生成树内 不构成回路
ans+=w;
Path.push_back({x,y,w});
}
}
}
int main(){
cin>>N>>M;
Edge.resize(M);
P.resize(N+1);
for(int i=0;i<=N;i++)
P[i]=i;
for(int i=0;i<M;i++)
cin>>Edge[i].from>>Edge[i].to>>Edge[i].w;
kruskal();
for(int i=0;i<Path.size();i++)
cout<<Path[i].from<<" : "<<Path[i].to<<"--"<<Path[i].w<<endl;
cout<<ans;
return 0;
}
//测试数据:
//9 15
//1 2 2
//1 7 3
//1 6 7
//2 3 4
//2 7 6
//3 8 2
//3 4 2
//4 5 1
//4 8 8
//5 6 6
//5 9 2
//6 9 5
//7 8 3
//7 9 1
//8 9 4