最小生成树MST
定义
在一个 ∣ V ∣ |V| ∣V∣个点的无向连通图中,取其中 ∣ V ∣ − 1 |V|-1 ∣V∣−1条边,并连接所有的顶点,所得到的子图称为原图的一棵生成树,当在一个图 G G G中的所有生成树的方案集合 S S S中,若有一棵生成树 T T T的所有边权之和比 S S S中其它生成树的边权之和都要小,那么就称 T T T为 G G G的MST即最小生成树。
最小边原则
图中权值最小的边(如果唯一的话)一定在最小生成树上。
唯一性原则
对于一个图 G G G,如果图中的边权值都不相同,则图的最小生成树是唯一的,反之不然。
- 对于任意一个权值,最小生成树上该权值的边数是相同的
- 对于任意最小生成树,当加上了所有权值小于 x x x 的所有边后,连通性是相同的
- 对于任意一条非树边,其对应树上路径权值和一定小于这条非树边的权值
Kruskal算法
核心思想
贪心,将边按权值排序,每次从剩下的边集中选择一条权值最小,而且两个顶点不在同一集合中的边加入生成树中,重复操作直至加入了 ∣ V ∣ − 1 |V|-1 ∣V∣−1条边。
算法实现
算法步骤:
- 给边排序
- 按权值从小到大选边,判断边的两个顶点是否在同一集合,如果不在同一集合,选择这条边,合并两个顶点所在的集合,直至选完了 ∣ V ∣ − 1 |V|-1 ∣V∣−1条边
算法过程图:
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,father[100005],cnt,ans;
struct Edge{
int from;
int to;
int dis;
}e[1000005];
/*==========并查集的相关操作==========*/
int findx(int x)//寻根函数
{
if(father[x]!=x) return findx(father[x]);
return father[x];
}
void unionx(int x,int y)//合并函数
{
x=findx(x);y=findx(y);
father[y]=x;
}
bool judge(int x,int y)//判断是否在同一集合
{
x=findx(x);y=findx(y);
if(x==y) return 1;
return 0;
}
/*================================*/
bool cmp(Edge a,Edge b)//排序函数
{
return a.dis<b.dis;
}
void Kruskal()//Kruskal算法
{
for(int i=1;i<=m&&cnt<n;i++)
if(!judge(e[i].from,e[i].to))//判断两个顶点是否在同一集合中
{
unionx(e[i].from,e[i].to);//合并两个集合
ans+=e[i].dis;//计入答案
cnt++;//选择|V|-1条边就够了
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) father[i]=i;//并查集初始化
for(int i=1;i<=m;i++)
cin>>e[i].from>>e[i].to>>e[i].dis;
sort(e+1,e+1+m,cmp);//给边排序
Kruskal();//Kruskal算法
cout<<ans<<endl;
return 0;
}
时间复杂度 O ( ∣ E ∣ log ∣ E ∣ + ∣ E ∣ α ( n ) ) O(|E|\log|E|+|E|\alpha(n)) O(∣E∣log∣E∣+∣E∣α(n)), α ( n ) \alpha(n) α(n)是一次并查集的复杂度