贪心-Kruskal-并查集

概念
Kruskal:每次选择图中边权值最小的边加入最小生成树,并且加入的边不能构成回路.
判断加入的边是否会构成回路 ==> 采用并查集方法
并查集(UnionFindSet):是一种树型的数据结构,用于处理一些不想交集合的合并及查询问题
主要操作:
(1)初始化:把每个点所在集合初始化为其自身
(2)查找:查找两个元素所在的集合,即找祖宗
注意:查找时,采用递归的方法找其祖宗,祖宗集合号等于自己时即停止。在回归时,把当前结点到祖宗路径上的所有结点统一为祖宗的集合号。
(3)合并:如果两个元素的集合号不同,将两个元素合并为一个集合。
注意:合并时只需要把一个元素的祖宗集合号,改为另一个元素集合号。擒贼先擒王,只改祖宗即可。
贪心-Kruskal-并查集_第1张图片
Code:

#include
#include
#include 
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

你可能感兴趣的:(算法)