prim算法是通过找距离最近的节点来扩充最小生成树的,稠密图选择prim算法效率比较高,但是对于稀疏图呢,prim算法就显的比较鸡肋了。对于稀疏图,有一个叫做kruskal的算法。此算法求稀疏图的效率比较高,时间复杂度为O(ElogE)。
kruskal算法主要通过找最小的边来合并节点来一步步的生成树的,他一开始将每个节点看成一棵树,然后通过边的关系来进行节点的合并,这里需要对给出的边进行升序排序,然后再检查边所联系的两个节点是否在同一集合,如果不在同一集合则将两个集合合并,直到整个森林变为一颗树为止。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn=1005;
int n,m;
struct edge
{
int s,e;
int len;
};
edge e[maxn];
int a[maxn];
vector <edge> ve; //记录纳入的边
//初始化
int init ()
{
for (int i=1;i<=n;i++)
a[i]=i;
}
//查找父节点
int finds(int re)
{
if(re==a[re])
return re;
return a[re]=finds(a[re]);
}
//将两个集合联合
bool unit (int x,int y)
{
int temp1=finds(x);
int temp2=finds(y);
if(temp1!=temp2)
{
a[temp1]=temp2;
return true;
}
return false;
}
int compare (edge a,edge b)
{
return a.len<b.len;
}
//krusal算法
int kruskal ()
{
//先进行排序
sort(e,e+m,compare);
int num=n;
int sum=0;
for (int i=0;i<m&&num>1;i++)
{
//判断是否在同一集合中,在同一集合就不再计入
if(unit(e[i].s,e[i].e))
{
ve.push_back(e[i]);
sum+=e[i].len;
num--;
}
}
if(num==1)
printf("最小生成树的权值和为%d\n",sum);
else
printf("不存在最小生成树\n");
}
//遍历纳入的边
void traverse ()
{
printf("纳入的边为:\n");
for (int i=0;i<ve.size();i++)
printf("%d<->%d 长度为%d\n",ve[i].s,ve[i].e,ve[i].len);
}
int main()
{
scanf("%d%d",&n,&m);
init();
for (int i=0;i<m;i++)
{
scanf("%d%d%d",&e[i].s,&e[i].e,&e[i].len);
}
kruskal();
traverse ();
return 0;
}
/*运行结果
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
最小生成树的权值和为12
纳入的边为:
3<->5 长度为1
3<->6 长度为1
1<->6 长度为2
2<->5 长度为2
2<->4 长度为6*/