最小生成树Kruskal算法学习笔记

最小生成树MST

定义

在一个 ∣ V ∣ |V| V个点的无向连通图中,取其中 ∣ V ∣ − 1 |V|-1 V1条边,并连接所有的顶点,所得到的子图称为原图的一棵生成树,当在一个图 G G G中的所有生成树的方案集合 S S S中,若有一棵生成树 T T T的所有边权之和比 S S S中其它生成树的边权之和都要小,那么就称 T T T G G G的MST即最小生成树。

最小边原则

图中权值最小的边(如果唯一的话)一定在最小生成树上。

唯一性原则

对于一个图 G G G,如果图中的边权值都不相同,则图的最小生成树是唯一的,反之不然。

  1. 对于任意一个权值,最小生成树上该权值的边数是相同的
  2. 对于任意最小生成树,当加上了所有权值小于 x x x 的所有边后,连通性是相同的
  3. 对于任意一条非树边,其对应树上路径权值和一定小于这条非树边的权值

Kruskal算法

核心思想

贪心,将边按权值排序,每次从剩下的边集中选择一条权值最小,而且两个顶点不在同一集合中的边加入生成树中,重复操作直至加入了 ∣ V ∣ − 1 |V|-1 V1条边。

算法实现

算法步骤:

  1. 给边排序
  2. 按权值从小到大选边,判断边的两个顶点是否在同一集合,如果不在同一集合,选择这条边,合并两个顶点所在的集合,直至选完了 ∣ V ∣ − 1 |V|-1 V1条边

算法过程图:
在这里插入图片描述
代码如下:

#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(ElogE+Eα(n)) α ( n ) \alpha(n) α(n)是一次并查集的复杂度


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值