最小生成树入门

定义

1、生成树

生成树本质上是一个图的子图,它的特性如下

  • 它是一颗无根树
  • 它包含原图的所有节点

听着可能有点抽象,说大白话,就是他是一个图删掉若干条边形成的树

当然,一个图的生成树可能有很多

2、最小生成树

在若干个生成树中边权和最小的,该树成为这个图的最小生成树

算法一 Prim(加点法)

算法分析

加点法,顾名思义,每次找到最优的点加入ans,加n次就形成了一个最小生成树

该算法基于贪心思想,核心在于维护d数组:d[x]表示点x到已经在最小生成树中的点中距离最短(边权最小)的点的距离。

n次找点中,每次找到未加入最小生成树的点中d[i]最小的值加入即可

如何维护d数组呢

就在找到最优点x后,遍历x子节点,用点x到其距离更新d数组即可

优化

显然这样时间复杂度来到了n^2,非常难受

这个算法很像最短路dijkstra,所以,同样的,用堆优化找最小值那一步

最后的时间复杂度就是mlogn啦

结合代码理解吧~

代码

 void Prim(int s){//起点编号s
	memset(d,0x3f,sizeof(d));//求min先赋最大
	memset(vis,0,sizeof(vis));//标记数组
	d[s]=0;
	q.push(node(s,0));
	while(!q.empty()){
		int u=q.top().first;//取出最优点
		q.pop();
		if(vis[u]){
			continue;
		}
		ans+=d[u];
		vis[u]=1;//标记
		for(int i=0;i<G[u].size();i++){//遍历子节点
			int v=G[u][i].v;
			long long w=G[u][i].w;
			if(d[v]>w&&!vis[v]){
				d[v]=w;
				q.push(node(v,w));//更新d数组
			}
		}
	}
}

算法二 Kruskal(加边法)

个人认为此思路更容易理解

算法分析

既然可以加点,那也可以加边。

那么如何加边呢,很容易就想到排序。所以将所有边按权值先排个序。

但如果仅按此累加的话,稍微思考便会发现,会出现环(也可以说是多余边,将连通的节点再连起来)。

所以,我们需要一个东西在加边时判断两点是否已经直接或间接的连通了。

——并查集。

在每次加边时维护并查集,加边前判断,n个点加n-1条边,思路达成!

代码

#include<bits/stdc++.h>

using namespace std;
const int M=2e5+10,inf=1e9+10;
int n,m,tt,ans,f[M];
struct edge{
	int v,u,w;
}edg[M*2];
bool cmp(edge x,edge y){
	return x.w<y.w;//按边权排序
}
int findd(int x){
	if(x==f[x]) return x;
	return f[x]=findd(f[x]);//并查集基操
}
void kruskal(){
	for(int i=1;i<=m;i++){
		int f1=findd(edg[i].u),f2=findd(edg[i].v);
		if(f1==f2) continue;//是否已经连通
		f[f2]=f1;//维护并查集
		ans+=edg[i].w;//记录答案
		tt++;
		if(tt==n-1) break;//加n-1条边
	}
}
int main()
{
    cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>edg[i].u>>edg[i].v>>edg[i].w;
	}
	sort(edg+1,edg+m+1,cmp);//排序
	for(int i=1;i<=n;i++) f[i]=i;//并查集初始化
	kruskal();
	cout<<ans;
	
    return 0;
}

算法时间复杂度易得为O(mlogm)

总结

两种算法本人更喜欢Kr(写起来更方便),分析时间复杂度,Prim在处理稠密图(尤其是完全图)时更有优势。

~~~---:)))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值