最小生成树超详细介绍

news2024/11/15 8:46:51

目录

一.最小生成树的介绍

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)


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

本篇完~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何更改默认浏览器?

打开设置---应用---默认应用 点击你想要设置为默认浏览器的应用&#xff08;假设为Microsoft Edge&#xff09;&#xff0c;点击设置默认值就可以了。

简单实践 spring clound 使用openfeign

1.概要 这是在前面工程基础上的一个变更。 前工程&#xff1a;检查实验 spring cloud nacos nacos-server-2.3.0-CSDN博客 2 代码 2.1 引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-open…

一般系统的请求认证授权思路【gateway网关+jwt+redis+请求头httpheader】

gateway&#xff1a;网关&#xff0c;我们都知道网关的作用就是对系统的所有请求&#xff0c;网关都会进行拦截&#xff0c;然后做一些操作&#xff08;例如&#xff1a;设置每个请求的请求头httpHeader&#xff0c;身份认证等等&#xff09;此时一般会使用到网关过滤器&#x…

【chromium】vs2022 中 clang构建

vs2022 “D:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe”看起来没装安装 clang 组件 vs2019 装的是12:报错了 Build started... 1>------ Build started: Project: ebBase, Configuration: Debug Win32 ------ 1>d:\Program Files…

【并发编程】手写线程池阻塞队列

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳重求进&#xff0c;晒太阳 示意图 步骤1&#xff1a;自定义任务队列 变量定义 用Deque双端队列来承接任务用ReentrantLock 来做锁并声明两个条件变量 Condition fullWai…

AE2023 After Effects 2023

After Effects 2023是一款非常强大的视频编辑软件&#xff0c;提供了许多新功能和改进&#xff0c;使得视频编辑和合成更加高效和灵活。以下是一些After Effects 2023的特色功能&#xff1a; 新合成预设列表&#xff1a;After Effects 2023彻底修改了预设列表&#xff0c;使其…

《权力的游戏》AI创作大电影 震撼来袭

《权力的游戏》AI创作大电影 震撼来袭 Westeros, a world of power, intrigue, and dark magic. In the frozen North, the army of the dead marches towards Winterfell. The Seven Kingdoms are divided by war and bloodshed, as the Great Houses battle for supremacy.…

必看!嵌入式基于UART的通信协议-RS232、RS485协议解析

这两种都是串口通讯的变种&#xff0c;为了提升串口通信的距离和稳定性。通常来说&#xff0c;正常的串口通信使用的是TTL电平&#xff0c;即高电平为2.4-5V&#xff0c;低电平为0-0.4V。高低电平之间的范围很小&#xff0c;如果有静电或者其他外界的干扰&#xff0c;很快会将低…

了解这六种最佳移动自动化测试工具吗?

最好的移动自动化测试工具 在本文章关于移动应用程序测试的这一部分中&#xff0c;我们将研究 2023 年 6 种最佳移动自动化测试工具。 1、Appium Appium 是一个非常流行的开源自动化测试框架&#xff0c;支持各种操作系统的自动化。它可以与本机、混合和移动 Web 应用程序一…

春运开始,北斗卫星助力盲区来车预警提示

春运开始&#xff0c;北斗卫星助力盲区来车预警提示 近期春运开始&#xff0c;高德地图启动了2024年的“温暖回家路”服务计划&#xff0c;通过数字化服务创新保障春运出行。除了具备自学习能力的新能源导航首发亮相外&#xff0c;还重点升级了盲区会车预警服务。在山区弯道、…

docker复习笔记01(小滴课堂)安装+部署mysql

查看内核版本。 关闭防火墙&#xff1a; 查看docker版本&#xff1a; 下载阿里yum源&#xff1a; 再看一下yum版本都有哪些&#xff1a; 我们可以看的docker-ce了。 安装它&#xff1a; 设置docker服务开机启动&#xff1a; 更新日志文件&#xff1a; 启动docker&#xff1a; …

ETL是什么,有哪些ETL工具?就业前景如何?

ETL是什么 ETL&#xff08;Extract-Transform-Load&#xff09;&#xff0c;用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目标端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象并不限于数据仓库。它可以自动化数据处理过程&#xff0c;减少…

2024/2/5总结

微信小程序 监听对象中所有属性的变化 如果某个对象中需要被监听的属性太多&#xff0c;为了方便&#xff0c;可以使用 通配符 ** 来监听 对象中所有属性的变化 什么是纯数字字段 概念&#xff1a;纯数字字段指的是那些不用于界面渲染的 data 字段。 好处&#xff1a;提升界面…

2024.02 国内认知大模型汇总

概述 大模型&#xff0c;又称为大规模机器学习模型&#xff0c;是一种基于大数据的人工智能技术。它通过深度学习和机器学习的方法&#xff0c;对大量数据进行训练&#xff0c;以实现对复杂问题的高效解决。大模型技术在语音识别、图像识别、自然语言处理等领域有着广泛的应用…

sqli.bypass靶场本地小皮环境(1-5关)

1、第一关 http://sqli.bypass/index1.php 单引号报错id1 双引号正常id1&#xff0c;应该是单引号闭合 id1--注释符用不了&#xff0c;%20和都用不了 %0a可以用 没有报错&#xff0c;用布尔盲注&#xff0c;POC&#xff1a;id1%0aand%0asubstr(ss,1,1)s%0aand%0a11 脚本跑数…

JavaScript流程控制详解之顺序结构和选择结构

流程控制 流程控制&#xff0c;指的是控制程序按照怎样的顺序执行 在JavaScript中&#xff0c;共有3种流程控制方式 顺序结构选择结构循环结构 顺序结构 在JavaScript中&#xff0c;顺序结构是最基本的结构&#xff0c;所谓的顺序结构&#xff0c;指的是代码按照从上到下、…

【python数据分析基础】—dataframe中index的相关操作(添加、修改index的列名、修改index索引值等)

文章目录 前言一、添加、修改index的列名二、修改index索引值 前言 本文主要讲dataframe结构中index的相关操作&#xff0c;index相当于是数据表的行。 一、添加、修改index的列名 新建一个dataframe表&#xff0c;我们可以自定义index的值&#xff0c;如下&#xff1a; imp…

Webpack源码浅析

webpack启动方式 webpack有两种启动方式&#xff1a; 通过webpack-cli脚手架来启动&#xff0c;即可以在Terminal终端直接运行&#xff1b; webpack ./debug/index.js --config ./debug/webpack.config.js通过require(webpack)引入包的方式执行&#xff1b;其实第一种方式最终…

sqli-labs-master靶场训练笔记(38-53|boss战)

2024.2.4 level-38 &#xff08;堆叠注入&#xff09; 这题乍一看感觉又是来卖萌的&#xff0c;这不是和level-1一模一样吗 然后仔细看了一下源代码&#xff0c;根据 mysqli_multi_query 猜测这题的本意应该是堆叠注入 mysqli_multi_query() 是 PHP 中用于执行多个 SQL 查…

Sysbench 性能测试(小白快速上手)

文末有惊喜哦 &#xff01; Sysbench 介绍 Sysbench 是一个在Linux系统上进行性能测试和基准测试的工具。它可以用于评估计算机系统的各种性能指标&#xff0c;如 CPU 性能、内存性能、文件 I/O性 能和数据库性能等。Sysbench 提供了多种测试模式和选项&#xff0c;可以帮助用户…