最小生成树超详细介绍

目录

一.最小生成树的介绍

1.最小生成树的简介

2.最小生成树的应用

3.最小生成树的得出方法

二.Kruskal算法

1.基本思想:

2.步骤:

3.实现细节:

4.样例分析:

5.Kruskal算法代码实现:

三.Prim算法

1.基本思想:

2.步骤:

3.实现细节:

4.样例分析:

5.Prim算法代码实现

四.总结


一.最小生成树的介绍

1.最小生成树的简介

最小生成树(Minimum Spanning Tree,简称MST,在一个连通的无向图,最小生成树是指包含图中所有顶点的一棵树,且该树的所有边的权重之和最小,关键在于最小两个关键。

在上面的示例图中我们知道了生成的数是不能有闭环的,那么我们怎么去理解最小的意思。

其实在每个点之间相连接的边中是有边的长度的,如下:

我们需要找出的边构成生成树,并且包含图中的所有顶点,使得边的权重之和最小。

2.最小生成树的应用

那么我们很好奇,我们为什么要去寻找这么一个最小生成树呢?

设想,面对一个交通落后的城市

在当今经济发展迅速的阶段,国家肯定不允许城市之间没有一条公路将各个城市串通起来,但是也不会盲目乱铺设,因为人力和成本是很多的,那么串通每个城市之间的公路就是边,我们利用最小生成树将这些城市串通起来的同时,也减少了成本的浪费,这个就是最小生成树的应用之一。

最小生成树在现实应用中具有广泛的用途。一主要应用领域是网络设计,例如通信网络和计算机网络的规划。通过选择最小生成树,可以确保网络中的节点连接最优,减少通信成本。在城市规划中,最小生成树被用于设计交通网络,确保道路布局经济高效。电力系统规划也借助最小生成树,以建立最优的电力输送网络。此外,在电路板设计、社交网络分析以及物流规划等领域,最小生成树都能提供有效的解决方案,降低资源消耗,提高系统效率。这些应用反映了最小生成树作为一种优化工具在解决各种连接和布线问题上的重要性。

3.最小生成树的得出方法

找出最小生成树有这么两个方法。

Kruskal算法和Prim算法。

虽然是两个方法,但是都有使用了贪心的思想。

下面将对两个方法进行详介。

二.Kruskal算法

1.基本思想:

  • 将图中所有的边按照权值从小到大排序。
  • 从小到大遍历排序后的边,如果边的两个端点不在同一个连通分量中,则将这条边加入最小生成树中,并将这两个端点合并为一个连通分量。(也就是新加的边不能与其他边构成环)

2.步骤:

  • 对图中所有边按照权值进行排序。
  • 初始化一个空的最小生成树。
  • 依次考察排序后的边,如果加入某条边不形成环,则将其加入最小生成树中。

3.实现细节:

  • 使用并查集(Disjoint Set)来管理连通分量。
  • 可以通过贪心的思想逐步加入边,直到所有顶点都在同一连通分量中为止。(也就是加入的边等于所有的顶点-1)

4.样例分析:

我们以这个图为例:

1.先将边按照从小到大分好

2.初始一个空的最小生成树

3.慢慢从小到大填入边

当我们连接4--6时,此时闭成一个环,用红色代表这条边不加入。

此时加入的边为顶点数(7)-1,所以代表已经全部串通,所以最小生成树的边之和就是:

1+2+2+3+4+4=16

5.Kruskal算法代码实现:

首先可以建立一个结构体,里面放上边联通的两个节点和边的长度。

struct node{
	int x,y,w;
}a[1000];

因为Kruskal算法需要使用到并查集,所以下面是必不可少的Find函数。

int Find(int x)
{
	if(pre[x]==x) return x;
	return pre[x]=Find(pre[x]);
}

由于需要排序,我们这里直接使用c++的sort函数,加上自定义函数cmp

bool cmp(node &x,node &y)
{
	return x.w<y.w;
}

这样就构成了我们的核心函数Kruskal函数。

void Kruskal()
{
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++){
		int fx=Find(a[i].x);
		int fy=Find(a[i].y);
		if(fx==fy) continue;
		pre[fx]=fy;
		ans+=a[i].w;
		cnt++;
		if(cnt==n-1)
		break; 
	}
}

得到完整代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int x,y,w;
}a[1000];
int n,m,ans=0,cnt=0;
int pre[1000];
int Find(int x)
{
	if(pre[x]==x) return x;
	return pre[x]=Find(pre[x]);
}
bool cmp(node &x,node &y)
{
	return x.w<y.w;
}
void Kruskal()
{
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++){
		int fx=Find(a[i].x);
		int fy=Find(a[i].y);
		if(fx==fy) continue;//如果在一个集合就跳过 
		pre[fx]=fy;
		ans+=a[i].w;
		cnt++;
		if(cnt==n-1)//当加入的边等于顶点数-1,就停止循环 
		break; 
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		pre[i]=i;//初始化 
	}
	for(int i=1;i<=m;i++){
		cin>>a[i].x>>a[i].y>>a[i].w;
	}
	Kruskal();//进入核心代码 
	if(cnt==n-1)
	cout<<ans<<endl;//可以得到答案,直接输出 
	else 
	cout<<-1<<endl;//不可以接通 
	return 0;
}

我们输出来试试看,是不是得到答案16。

三.Prim算法

1.基本思想:

  • 选择一个起始顶点,然后逐步选择与当前生成树相邻的权值最小的边,并将连接的顶点加入生成树。
  • 重复以上步骤,直到生成树包含图中的所有顶点。

2.步骤:

  • 从任意顶点开始,初始化一个空的最小生成树。
  • 选择一个与当前生成树相邻的边中权值最小的边,将其连接的顶点加入最小生成树。
  • 重复上述步骤,直到最小生成树包含了所有顶点。

3.实现细节:

  • 使用优先队列(最小堆)来维护当前生成树与其余顶点之间的边。
  • 根据贪心策略,每次选择权值最小的边。

4.样例分析:

还是以这个图为例子

以0这个节点出发,我们有四条边可以加入。

我们选择这最小的。

然后从0和2这两个点出发,继续寻找,发现有两个2,我们选其中一个就行。

按照这种规则下去,我们得到了最小生成树,红色边是由于构成了闭环而没有使用的边,,绿色为使用的边。

我们来计算一下使用到的所有边的长度:

1+2+2+3+4+4=16。

我们发现这个就是我们前得到的答案。

5.Prim算法代码实现

首先生成最小堆。

struct node
{
	int u, w; // u是节点,w是花费
	bool operator < (node x) const
	{
		return w > x.w;
	} //重载运算符,生成最小堆
};

将每个顶点初始化为无穷大

	for(int i = 0; i < n; i++)
		dis[i] = INF; // 初始化dis[]

加边函数

void add(int u, int v, int w)
{
	a[++num].to = v;
	a[num].w = w;
	a[num].next = head[u];
	head[u] = num;
} // 加边

得到完整代码:

#include<bits/stdc++.h>
using namespace std;
const int INF = 1e9;
struct data
{
	int to, next, w;
} a[400002]; //链式前向星
struct node
{
	int u, w; // u是节点,w是花费
	bool operator < (node x) const
	{
		return w > x.w;
	} //重载运算符,生成最小堆
};
int dis[200010], head[200010], num;// dis是最小花费,head存边,num为边数
bool vis[200010];
int n, m, ans = 0, cnt = 0;
void add(int u, int v, int w)
{
	a[++num].to = v;
	a[num].w = w;
	a[num].next = head[u];
	head[u] = num;
} // 加边
priority_queue<node> q;
void Prim()
{
		q.push((node) {0, 0});
		while(!q.empty()||cnt < n) // 进行n-1次 
		{
			node x = q.top();
			q.pop();
			if(vis[x.u]) continue;
			vis[x.u] = true;
			cnt++;
			ans += x.w;
			for(int i = head[x.u]; i ; i = a[i].next) 
				if(a[i].w < dis[a[i].to]) {
					dis[a[i].to] = a[i].w;
					q.push((node) {a[i].to, a[i].w});
				}
		}
}
int main()
{
	cin>>n>>m;
	for(int i = 1; i <= m; i++)
	{
		int u, v, w;
		cin>>u>>v>>w;
		add(u, v, w); add(v, u, w); // 需要加两次无向图 
	}
	for(int i = 0; i < n; i++)
		dis[i] = INF; // 初始化dis[]
		Prim();
	if(cnt==n)
	cout<<ans<<endl; 
	else 
	cout<<"-1"<<endl; 
	return 0;
}

我们输出一下试试。

代码得到正确答案。

四.总结

  • Kruskal更适合稀疏图,因为它按照边的权值排序,而不考虑顶点的度。
  • Prim更适合稠密图,因为它按照顶点的度增长来选择边,每次选择与当前生成树相邻的最小权值边。
  • Kruskal的时间复杂度主要取决于对边进行排序的时间,通常为O(E log E)
  • Prim的时间复杂度通常为O(E log V)。

里面的E为边的数量,V为顶点的数量。

 是不是已经完美掌握了。

赶紧去练练题目,来巩固一下知识。

P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1194 买礼物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


最小生成树就介绍到这里。

本篇完~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/373671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【华为 ICT HCIA eNSP 习题汇总】——题目集12

1、企业网络内部常常采用私有 IP 地址进行通信&#xff0c;以下哪个地址属于私有 IP 地址&#xff1f; A、0.1.1.1 B、127.5.4.3 C、128.0.0.5 D、172.24.35.36 考点&#xff1a;网络层 解析&#xff1a;&#xff08;D&#xff09; A类 IP 地址中&#xff0c;10.0.0.0 ~ 10.255…

SpringSecurity(18)——OAuth2授权码管理

AuthorizationCodeServices public interface AuthorizationCodeServices {//为指定的身份验证创建授权代码。String createAuthorizationCode(OAuth2Authentication authentication);//使用授权码。OAuth2Authentication consumeAuthorizationCode(String code)throws Invali…

ACM训练题:Raising Modulo Numbers

主要意思就是上面的式子&#xff0c;求幂的和的模&#xff0c;求幂自然是快速幂&#xff0c;这里都带上模&#xff0c;求和的模也可以分开取模。 AC代码&#xff1a; #include <iostream> using namespace std; long long Z,M,H,a,b; long long quick_mi(long long x,l…

【数据结构与算法】(11)基础数据结构 之 二叉树 二叉树的存储与遍历及相关示例 详细代码讲解

目录 2.10 二叉树1) 存储2) 遍历广度优先深度优先递归实现非递归实现 习题E01. 前序遍历二叉树-Leetcode 144E02. 中序遍历二叉树-Leetcode 94E03. 后序遍历二叉树-Leetcode 145E04. 对称二叉树-Leetcode 101E05. 二叉树最大深度-Leetcode 104E06. 二叉树最小深度-Leetcode 111…

R语言:箱线图绘制(添加平均值趋势线)

箱线图绘制 1. 写在前面2.箱线图绘制2.1 相关R包导入2.2 数据导入及格式转换2.3 ggplot绘图 1. 写在前面 今天有时间把之前使用过的一些代码和大家分享&#xff0c;其中箱线图绘制我认为是非常有用的一个部分。之前我是比较喜欢使用origin进行绘图&#xff0c;但是绘制的图不太…

C++杂选

#include <iostream> #include <regex>using namespace std;int main() { //它声明了一个 string 类型的变量 input&#xff0c;用于存储输入的字符串。然后使用 getline() 函数从标准输入中读取一行输入&#xff0c;并将其存储在 input 变量中。string input;getl…

PAT-Apat甲级题1008(python和c++实现)

PTA | 1008 Elevator 1008 Elevator 作者 CHEN, Yue 单位 浙江大学 The highest building in our city has only one elevator. A request list is made up with N positive numbers. The numbers denote at which floors the elevator will stop, in specified order. It …

c#读取csv文件中的某一列的数据

chat8 (chat779.com) 上面试GPT-3.5,很好的浏览网站&#xff0c;输入问题&#xff0c;可得到答案。 问题1&#xff1a;c#如何在csv中读取某一列数据 解答方案&#xff1a;在 C#中&#xff0c;你可以使用File.ReadAllLines来读取CSV中的所有行&#xff0c;然后逐行解析每一行…

机器学习---概率图模型(隐马尔可夫模型、马尔可夫随机场、条件随机场)

1. 隐马尔可夫模型 机器学习最重要的任务是根据已观察到的证据&#xff08;例如训练样本&#xff09;对感兴趣的未知变量&#xff08;例如类别标 记&#xff09;进行估计和推测。概率模型&#xff08;probabilistic model&#xff09;提供了一种描述框架&#xff0c;将描述任…

网络选择流程分析(首选网络类型切换流程)

首先是界面,我在此平台的界面如下: 对应的入口源码位置在Settings的UniEnabledNetworkModePreferenceController中,当然其他平台可能在PreferredNetworkModePreferenceController中,流程上都是大同小异 然后点击切换按钮会调用到UniEnabledNetworkModePreferenceControlle…

智算中心建设主流加速卡选型策略

智算中心建设主流加速卡选型对比 —— 加速卡H800、A800、L40S、***B 一、加速卡基本性能比较 序号比较项H800A800L40S某国产NPU&#xff08;本文简称“nB”&#xff09; 1 加速卡类型 GPU GPU GPU NPU 2 供应商 英伟达 英伟达 英伟达 - 3 FP32&#xff08;TFLO…

MySQL查询优化技巧和10个案例展示

优化MySQL查询的实战技巧&#xff1a; **避免使用SELECT ***&#xff1a;只获取需要的列&#xff0c;这样可以减少数据传输量&#xff0c;提高查询效率。使用索引&#xff1a;为查询频繁的列创建索引&#xff0c;可以显著提高查询速度。但请注意&#xff0c;索引并非万能&…

Android中设置Toast.setGravity()了后没有效果

当设置 toast.setGravity()后&#xff0c;弹窗依旧从原来的位置弹出&#xff0c;不按设置方向弹出 类似以下代码&#xff1a; var toast Toast.makeText(this, R.string.ture_toast, Toast.LENGTH_SHORT)toast.setGravity(Gravity.TOP, 0, 0)//设置toast的弹出方向为屏幕顶部…

绕过安全狗

本节我们想要绕过的安全狗版本为v4.023957 &#xff0c;它是网站安全狗的Apache版。 首先搭建环境。渗透环境选用DVWA漏洞集成环境&#xff0c;下载地址 为http://www.dvwa.co.uk/ 。DVWA是一款集成的渗透测试演练环境&#xff0c;当刚刚入门 并且找不到合适的靶机时&#xff…

Bytebase 签约 Vianova,助力欧洲城市交通智能平台中 Snowflake 和 PG 的变更自动化及版本控制

在数字化发展的浪潮中&#xff0c;自动化数据库变更管理成为提升产品上线效率、降低人为失误风险的关键工具&#xff0c;同时促进流程的一致性与标准化&#xff0c;确保合规性和变更的可追溯性。近日&#xff0c;数据库 DevOps 团队协同管理工具 Bytebase 签约欧洲交通数据管理…

H12-821_134

134.如图所示&#xff0c;RED在入方向调用了ip as-path-filter1&#xff0c;那么路由10.0.0.0/24会从路径_________被RE_D学习。(请填写1或2) 答案&#xff1a;1 注释&#xff1a; ip as-path-filter 1解释&#xff1a; ip as-path-filter 1 deny _300$ 拒绝AS300始发的路由&…

图像异或加密、解密的实现

很多论文提到了从左上角开始做异或,逐行推导得到结果。 解密过程是加密的逆过程。 先看其基本方法: 参考文献: A Chaotic System Based Image Encryption Scheme with Identical Encryption and Decryption Algorithm 大多数论文都用了这个思路,我们使用MATLAB实现代码…

ASUS华硕灵耀X双屏UX8402V工厂模式原厂Win11.22H2系统安装包,含WinRE恢复出厂时开箱状态自带预装OEM系统

适用型号&#xff1a;UX8402VV、UX8402VU 链接&#xff1a;https://pan.baidu.com/s/1D7tJshKTNFYO4YyzKX0ppQ?pwd3saf 提取码&#xff1a;3saf Zenbook Pro灵耀X笔记本电脑原装出厂Windows11系统 带有ASUS RECOVERY恢复功能、自带面部识别&#xff0c;声卡&#xff0c;网…

PySpark(四)PySpark SQL、Catalyst优化器、Spark SQL的执行流程

目录 PySpark SQL 基础 SparkSession对象 DataFrame入门 DataFrame构建 DataFrame代码风格 DSL SQL SparkSQL Shuffle 分区数目 DataFrame数据写出 Spark UDF Catalyst优化器 Spark SQL的执行流程 PySpark SQL 基础 PySpark SQL与Hive的异同 Hive和Spark 均是:“分…

SpringBoot-基础篇03

之前搭建了整个开发环境实现了登录注册&#xff0c;springBoot整合mybatis完成增删改查&#xff0c;今天完成分页查询&#xff0c;使用阿里云oss存储照片等资源&#xff0c;后期会尝试自己搭建分布式文件系统来实现。 一&#xff0c;SpringBootMybatis完成分页查询 1&#xff…