Kruskal算法
kruskal算法是从边出发,先将所有边的权值从小到大排序,然后依次添加进图中,在添加过程中要判断是否形成环。
代码实现Kruskal算法
四步法
1、先将所有的边拿出来,按权值进行排序
2、遍历所有的边
3、在遍历过程中利用并查集FindFather操作判断是否形成环
4、如果遍历的边数量为n-1(n个结点,n-1条边)停止遍历求出最小生成树。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=200020;
int n,m,ecnt;//n个结点、m组关系、ecnt第几条边
int head[maxn],f[maxn];//head用于存放头边、f用于并查集存放父亲结点编号
struct edge
{
int u,v,w,next;//起始结点u,终止结点v,权值w,next
} E[maxn<<1];//两个点最多两条边,所以点数x2
bool operator<(const edge &x,const edge &y)//重载操作符<
{
return x.w<y.w;
}
int findFa(int x)
{
return f[x]==x?x:f[x]=findFa(f[x]);//带路径压缩的并查集find操作
}
//给边添加信息u,v,w,以及前一条边
void addedge(int u,int v,int w)
{
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].next=head[u];
head[u]=ecnt;
}
int kruskal()
{
int sum=0,cnt=0;//sum求生成树的权值和,cnt计数统计了多少条边
sort(E+1,E+1+ecnt);
//遍历所有边,找出不能成环的
for(int i=1; i<=ecnt; i++)//注意是ecnt不是m,因为是双向边
{
int u=E[i].u;
int v=E[i].v;
int fx=findFa(u);//u的父亲节点fx
int fy=findFa(v);//v的父亲结点fy
if(fx!=fy)
{
cnt++,sum+=E[i].w,f[fx]=fy;//将fx的父亲结点指向y的父亲节点
}
if(cnt==n-1)break;//n个点的生成树只有n-1条边
}
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
//初始化并查集
for(int i=1; i<=n; i++)f[i]=i;
for(int i=1; i<=m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
//无向图需要添加两个
addedge(u,v,w);
addedge(v,u,w);
}
printf("%d\n",kruskal());
return 0;
}
复杂度:
O(ElogE)(与边数目相关)
应用场景:
kruskal算法从复杂度来看,适合用于边少点多的稀疏图的情况,而边多点少用prim算法