首先,这两个算法都是求最小生成树的算法。
首先,生成树是建立在无向图中的,对于有向图,则没有生成树的概念,所以接下来讨论的图均默认为无向图。对于一个有n个点的图,最少需要n-1条边使得这n个点联通,由这n-1条边组成的子图则称为原图的生成树。一般来说,一个图的生成树并不是唯一的(除非原图本身就是一棵树)。
在实际生活中我们常常会遇到这样一些问题:有若干个需要连接的点(不妨假设为一些村庄)和若干条连接着两个点的边(在村庄间修公路),而这些边会有不同的权值(可设为修路所需的费用不同)。现在要连通这些所有的点,并使权值和最小。这类问题在现实生活中很广泛,如修公路、架设电网,等等。
在信息学竞赛中,这种问题有专门的称谓“最小生成树”(Minimum Spanning Tree,简称MST)。
Kruskal算法:
顶点个数:n,边的条数:m
基本思想:
运用贪心的思想,每次选取权值最小的边,通过判断这条边所连接的两个顶点是否属于同一个集合,来决定这条边是否加入到生成树中去,如果不属于同一个集合,就加入到生成树中去,否则,就不加到生成树中去。直到有n-1条边加入到生成树中去(或者所有的边都访问完成了)就结束算法。
算法执行过程:
一:将边的所有权值按从小到大排序。
二:按照边的权值从小到大遍历所有的边。
三:每次遍历边的时候运用并查集判断边的两个顶点是否属于同一个集合,如果不属于同一个集合,那么就将这条边加入到生成树中去。
四:每次遍历边完成后,判断一下生成树中的边是否等于n-1,如果等于,直接输出结果,结束算法。如果不等于n-1,继续遍历其他的边。
代码如下:
#include<bits/stdc++.h>
#define MAXN 9999999
using namespace std;
int n,m;//n-->顶点个数 m-->边个数
int father[MAXN];//father[i]代表编号为i的顶点的父亲顶点的编号是多少
int sum=0;//加权总和
int n1=0;//最小生成树的边的条数
struct node
{
int u,v,w;
}a[MAXN];
bool cmp(node x1,node x2)//比较器
{
return x1.w<x2.w;
}
int getfather(int x)//找x节点的父亲节点的函数
{
if(father[x]==x)//自己就是自己的父亲节点
return x;
father[x]=getfather(father[x]);//递归寻找父亲节点,并且压缩路径
return father[x];//返回x节点的父亲节点的编号
}
void Kruskal()
{
for(int i=1;i<=n;i++)//初始化父亲节点数组
father[i]=i;
sort(a,a+m,cmp);//按照每个边的权值从小到大排序
for(int i=0;i<m;i++)//从边权值最小的边开始依次访问所有边
{
if(getfather(a[i].u)!=getfather(a[i].v))//这条边连接的两个顶点的父亲顶点不是同一个
{
sum+=a[i].w;
n1++;
father[a[i].v]=a[i].u;
}
if(n1==n-1)//由树的性质可知,树的边的条数是顶点数减一
{
cout<<sum<<endl;
return ;
}
}
return ;
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
cin>>a[i].u>>a[i].v>>a[i].w;
Kruskal();
}
代码运行过程:
原图:
sum=0
第一步:
sum=1 m1=1<n-1
第二