克鲁斯卡尔算法 最小生成树
1.基本思想
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止
2.使用查并集算法
并查集就是把每堆元素合并为一个具有相同父节点的集合,如果两堆元素父节点不同,说明他们不连通,反之连通
int pre[N];
void init(int n)
{
for(int i=0;i<n;i++)
pre[i]=i;
}
//初始化函数 使得每个节点的父节点为其本身
//即 各个节点独立
int find(int x)
{
int r=x,i=x,temp;
if(pre[r]==r)
return r;
while(pre[r]!=r)
r=pre[r];
while(i!=r)
{
temp=pre[i];
pre[i]=r;
i=temp;
}
//进行路径压缩
return r;
}
3.克鲁斯卡尔算法
#include<bits/stdc++.h>
using namespace std;
struct Edge
{
int u,v,w;
}
const int max=100000+10;
int pre[max];
Edge edge[max];
bool compare(Edge e1,Edge e2)
{
return e1.w<e2.w;
}
void init(int n)
{
for(int i=0;i<n;i++)
pre[i]=i;
}
//初始化函数 使得每个节点的父节点为其本身
//即 各个节点独立
int find(int x)
{
int r=x,i=x,temp;
if(pre[r]==r)
return r;
while(pre[r]!=r)
r=pre[r];
while(i!=r)
{
temp=pre[i];
pre[i]=r;
i=temp;
}
//进行路径压缩
return r;
}
//找到其父节点
void Kruskal(int n)
{
int i,sum,total,fx,fy;
total = 1;
sum = 0;
i = 0;
while(total<n)
{
fx = find(edge[i].u);
fy = find(edge[i].v);
if(fx!=fy)
{
pre[fx] = fy;
total++;
sum +=edge[i].w;
//最小生成树所有边之和
}
//判断是否形成环
//fx == fy 则现在是有一个环 ,因此要寻找下一条边
i++;
}
cout<<sum;
}
int main()
{
int n,m;
int i,x,y,z;
cin>>n>>m;
for(i=0;i<m;i++)
{
cin>>edge[i].u;
cin>>edge[i].v;
cin>>edge[i].w;
}
sort(edge,edge+m,compare);
init(n);
Kruskal(n);
}
4.小结
-
在克鲁斯卡尔算法中,从最小边开始,依次判断新加一条边是否满足要求 即 不能形成环
-
判断是否有环 则需要 边的结构体 和 查并集(包含压缩算法) 结合使用
-
使用 sort函数 并且 指定第三个参数 来对 边 从小到大排序
-
若该边满足条件,则加上该条边长度,最后输出 sum