克鲁斯卡尔算法–最小生成树
那个男孩不想写出自己的最小生成树,我叫AAA
通过这篇文章,你能看到:
-
克鲁斯卡尔算法的源代码
-
什么是最小生成树
-
克鲁斯卡尔算法所涉及到的其他算法
克鲁斯卡尔算法的源代码
源代码如下:
#include <iostream>
#include <algorithm>
#define MAXNP 100010
#define MAXNE 100010
using namespace std;
int Q[MAXNP];
struct edge{
int from,to,len;
}edges[MAXNE];
int cmp(edge a,edge b)
{
return a.len < b.len;
}
int find(int a)
{
int root = a;
while(root != Q[root])
{
root = Q[root];
}
while(root != a)
{
int tmp = Q[a];
Q[a] = root;
a = tmp;
}
return root;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1;i <= n;i++)
{
Q[i] = i;
}
for(int i = 1;i <= m;i++)
{
cin>>edges[i].from>>edges[i].to>>edges[i].len;
}
sort(edges+1,edges+1+n,cmp);
int cnt = 0,ans = 0;
for(int i = 1;i <= n,cnt < n - 1;i++)
{
int tmpto = find(edges[i].to);
int tmpfrom = find(edges[i].from);
if(tmpto != tmpfrom)
{
cnt++;
Q[tmpto] = tmpfrom;
ans += edges[i].len;
}
}
cout<<ans;
return 0;
}
这是一个编程初学者(我)写的代码,仅供参考。
什么是最小生成树
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n
个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。 --度娘
用我的理解来说:最小生成树就是把一个图砍掉一些边,变成边权重和最小的树。
那么,这里的边应该怎么砍?如何才能让砍掉的边是树中不需要的边?
这就是我们需要了解的了。
这里的边应该怎么砍?
我们可以通过局部贪心的算法目前来实现一味的砍掉最小边。
首先 将输入的边按照权值从小到大进行排序:
在边的结构体中,有三个数据:来的节点,去的节点,以及边的长度
struct edge{
int from,to,len;
}edges[MAXNE];
其中的节点都是在一维int数组Q中进行存储
int Q[MAXNP];
我们只需要写一个给快速排序用的函数,就可以进行排序了
int cmp(edge a,edge b)
{
return a.len < b.len;
}
快速排序不要忘记参数和头文件
sort(edges+1,edges+1+n,cmp);
但是这样一味砍掉最小的边有可能将树上有用的边砍掉
所以我们探讨到了第二个问题
如何才能让砍掉的边是树中不需要的边?
这里我们就需要了解一个至关重要的东西
并查集
我们来假设这样一个情景:
A和B是亲戚关系,B和C是亲戚关系,D和E是亲戚关系
如果他们互相了解彼此之间的关系的话,不出意外的情况下
A和C见面会互相寒暄,A和E不会
因为A和C都有一个亲戚是B,所以他们之间是亲戚关系
这就是一个小型的并查集问题
并查集问题的核心就是将一条链上的点都指向一个根节点,我们称之为
路径压缩
对于我们的问题,我们将插入的边的两个点放到集合里,然后将每个可能被插入的边的两个节点分别进行在集合中的搜索,如果搜索的结果是这两个节点有同一个根节点,我们就知道这两个集合已经连接在一起了,(因为我们已经是将边权值从小到大排序了,所以先前的边已经放进了树里,所以这里的边就不需要了)我们就可以对他说
“你的简历我已经看了,有没有被录用回家等通知吧”
然后依次遍历每条边,加上输入输出,我们的克鲁斯卡尔算法就完成了
并查集算法
int find(int a)
{
int root = a;
while(root != Q[root])
{
root = Q[root];
}
//找到根节点
while(root != a)
{
int tmp = Q[a];
Q[a] = root;
a = tmp;
}
//将此线上的节点指向的节点都指向根节点
return root;
}
关键语句
for(int i = 1;i <= n,cnt < n - 1;i++)
{
int tmpto = find(edges[i].to);//看看来的点的根节点
int tmpfrom = find(edges[i].from);//看看去的点的根节点
if(tmpto != tmpfrom)
{
cnt++;
Q[tmpto] = tmpfrom;//使用Q[a] = b代表a和b有联系
ans += edges[i].len;
}
}
结语
感谢你能看到这里,我是一个初二的学生,这是我第一次写博客。代码可能会出现一些问题。如果有问题,请多指教