【算法】图论——树的重心

目录

题目解析

算法原理

图的存储

算法实现 


题目解析

题目解析 

给定一颗树,树中包含n个结点(编号)和n-1条无向边。请找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。


什么是重心? 

 重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中节点的数量的最大值最小,那么这个节点被称为树的重心。 


补充:树和图的关系 

树本质上是一种特殊的图,它对应的是无环连通图。也就是说,在树中任意两个节点之间都存在路径(连通性),并且不存在回路(无环)。

树与一般图相比,有着如下性质:

  • 一般的图可以有环,节点之间的连接关系比较复杂。比如在一个有向图中,边是有方向的,从一个节点到另一个节点的路径可能受到边的方向限制;而树作为无向图,边没有方向限制,并且由于其无环的特性,从任意节点到另一个节点的路径是唯一的。
  • 从连通性角度看,树是保持图连通的最小结构。如果从树中去掉任何一条边,就会导致图不再连通,会划分出两个或更多的连通分量;而对于一般的连通图,可能存在冗余的边,去掉某些边后仍然可以保持连通。

重心举例

如下图,我们得到一个树结构:

 

若我们删除节点1,那么我们会得到三个连通图,如下:

  • 所谓的各个连通块中节点的最大值,指的是连通块之间的对比,在上图中,连通块节点数量最大值是4,即中间的连通块节点数量

重心是指我们遍历这张图中所有的节点,并尝试删除节点后所形成连通块节点数量的最大值是最小的。

举一个非重心的例子:如下图

 我们尝试删除节点2后,所形成的连通图一共有如下3个:

 很明显,连通块节点数量的最大值是6。

而尝试删除1的连通块节点数量的最大值是4,所以2一定不是树的重心

实际上,在上图中一共存在着两个重心,它们分别是1和4,感兴趣可以自己尝试算算


树的重心的性质 

  • 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
  • 删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
  • 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  • 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  • 一棵树最多有两个重心,且相邻。 

算法原理

我们要求得一棵树得重心,那么较为简单得一种方式是尝试计算每一个节点如果被删除后,所得的连通块数量的最大值,并取计算完后的最小值。

下图删除节点4为例,思考一下,若删除节点4后,所形成的连通块有哪些?

 

很明显,连通块只会出现在两种情况:

  • 根节点为4的树的所有子树都是连通块
  • 整棵树的根,刨除4这个子树所形成的连通块

那么如何获得根节点为4的树的所有子树的节点数量,以及4所对应的树的节点数量呢

我们只需DFS即可:

  • 遍历到9,9无子树,向上返回1
  • 遍历到3,3的子树的连通块节点数量为1,那么3对应的连通块节点数量就为2,3向上返回2
  • 遍历到6,6无子树,向上返回1
  • 遍历到4,4的子树的连通块节点数量为2+1=3,所以4对应的连通块中节点数量为3+1=4(包含4自身)

如何获得整棵树的根(抛出删除节点4对应子树)的连通块数量呢?

直接通过n-i计算即可

其中n表示的是总的节点数量,在上图中n即为9。

其中i表示的是以被删除节点为根的子树的连通块数量,在上图中i即为4


图的存储

图的存储我们采用邻接表

  • 邻接表是图(包括有向图和无向图)的一种链式存储结构。它主要用于存储图中节点之间的连接关系。
  • 对于图中的每个顶点,都有一个单链表,链表中的节点存储了与该顶点相邻接的顶点信息

举例 

假设,值为1的点指向了2、4、7。那么我们只需要构建一个单链表,表头节点是1,其余的表示的是1指向它

若每一个节点都有一张单链表,用来表示它所指向的元素,那么该存储结构我们称之为邻接表

若是无向图,那么当1指向2时,还需要添加一条2指向1的边


单链表的实现

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int ne[N], e[N], idx;
//ne存储的是next指针指向的节点编号
//e存储的是节点编号所对应的值
//idx用于分配一个唯一的节点编号

int main()
{
	int n; //n是节点的数量
	cin >> n;
	//下标为0的点是头节点
	ne[0] = -1;//初始化单链表,-1来表示空节点
	idx = 1; //节点编号从1开始,因为0已经分配给头节点了
	for (int i = 0; i < n; ++i)
	{
		int x;
		cin >> x;
		e[idx] = x; //分配一个节点编号给x 
		ne[idx] = ne[0];//当前节点指向头节点指向的节点
		ne[0] = idx++;//头节点指向当前节点
	}
	for (int i = ne[0]; i != -1; i = ne[i]) printf("%d ", e[i]);
	return 0;
}

邻接表的实现 

#include <iostream>
using namespace std;

const int N = 1e5 + 10;

int h[N], ne[N], e[N], idx;

void add(int a,int b)
{
	//1 3 表示1指向3,即h[1]头插3
	//添加一条a->b的边
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

int main()
{
	int n; //边的数量
	cin >> n;
	memset(h, -1, sizeof h); //初始化邻接表
	for (int i = 0; i < n; ++i)
	{
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	return 0;
}

算法实现 

第一步:根据题目要求,我们首先把输入进来的无向图保存起来,题目的输入如下:

  • 第一行:输入的是n
  • 第2-n行:输入两个数字a,b。表示a和b之间有一条无向边 

具体代码参考邻接表的实现

第二步:深度优先遍历

dfs中有一个参数u在dfs期间我们也一并更新最终的结果,定义变量ans表示最终的返回值

dfs函数要完成的功能是获得以u为根的子树形成的连通块节点数量,并更新根节点ans

注意:由于每一个节点我们只需要遍历一次,所以我们定义一个bool数组st,若st[i]=true,表示该节点已经被遍历过了,反之未遍历过

#include <iostream>
using namespace std;

const int N = 1e5 + 10;

int h[N], ne[N], e[N],st[N], idx;
int ans = N,n; //n表示边的数量 ,ans表示删除结果

void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
int dfs(int u)
{
	int res = 0; //res表示删除点u后连通块节点数量最大值
	int sum = 1; //sum表示要返回给上一层的以u为根的子树节点数量,初始化为1因为至少有u一个节点
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!st[j])
		{
			st[j] = true;
			int s = dfs(j);
			res = max(res, s); //连通块节点数量最大值可能出现在子树中
			sum += s;
			st[j] = false;
		}
	}
	res = max(res, n - sum); //最大值可能出现在根节点抛去u子树后的连通块节点数量
	ans = min(res, ans); //u可能是重心,更新一下
	return sum;
}
int main()
{

	cin >> n;
	memset(h, -1, sizeof h); //初始化邻接表
	for (int i = 0; i < n-1; ++i)
	{
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	dfs(1);
	printf("%d", ans);
	return 0;
}

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

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

相关文章

【Vue3 ElementUI开发环境搭建】

VUE搭建关系系统 1. 安装vue脚手架工具2. 使用脚手架创建项目2.1 选择VUE版本2.2 启动demo2.3 vue工程搭建完的目录 3. 安装Element UI3.1 测试ElementUI3.1.1 更换Demo页面的内容3.1.2 引入ElementUI的样式表 1. 安装vue脚手架工具 npm install -g vue/cli执行命令后等他跑一…

Redis常见问题总结

Redis常见问题总结 1.Redis分布式存储方案 分布式存储核心特点主从&#xff08;Master/Slave&#xff09;模式一主多从&#xff0c;故障时手动切换。哨兵&#xff08;Sentinel&#xff09;模式有哨兵的一主多从&#xff0c;主节点故障自动选择新的主节点。集群&#xff08;Cl…

Yeeco成长型一体化数智赋能平台:科技矩阵重塑企业数字生态

随着科技的飞速发展&#xff0c;我们正在步入一个被称为“数智化时代”的新时代。在这个时代中&#xff0c;数据处理和分析的能力被提升到一个前所未有的高度&#xff0c;而这种变化背后的重要推动力量就是各种新兴的技术趋势。 为了在激烈的市场竞争中脱颖而出&#xff0c;Yee…

STM32 DMA直接存储器存取原理及DMA转运模板代码

DMA简介&#xff1a; 存储器映像&#xff1a; 注意&#xff1a;FLASH是只读的&#xff0c;DMA不能写入&#xff0c;但是可以读取写到其他存储器里 变量是存在运行内存SRAM里的&#xff0c;常量&#xff08;const&#xff09;是放在程序存储器FLASH里的 DMA框图&#xff1a; …

保护数字资产:iOS 加固在当前安全环境中的重要性

随着互联网和手机的发展&#xff0c;APP在我们的日常生活中已经变得无处不在&#xff0c;各大平台的应用程序成为了黑客攻击的主要目标。尤其在 2024 年&#xff0c;随着数据泄露和隐私侵犯事件的频发&#xff0c;手机应用的安全问题再次成为公众关注的焦点。近期&#xff0c;多…

【数据结构】动态规划-基础篇

针对动态规划问题&#xff0c;我总结了以下5步&#xff1a; 确定dp数组以及下标的含义&#xff1b; 递推公式&#xff1b; dp数组如何初始化&#xff1b; 遍历顺序&#xff1b; 打印dp数组&#xff08;用来debug&#xff09;&#xff1b; 以上5步适用于任何动态规划问题&#x…

LeetCode 动态规划 组合总和 IV

组合总和 IV 给你一个由 不同 整数组成的数组 nums &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3], target 4 输出&#xff1a;7 …

数据结构(栈Stack)

1.前言&#xff1a; 在计算机科学中&#xff0c;栈&#xff08;Stack&#xff09;是一种基础而存在的数据结构&#xff0c;它的核心特性是后进先出&#xff08;LIFO&#xff0c;Last In, First Out&#xff09;。想象一下&#xff0c;在现实生活中我们如何处理一堆托盘——我们…

如何抽象策略模式

策略模式是什么 策略设计模式&#xff08;Strategy Pattern&#xff09;是一种面向对象设计模式&#xff0c;它定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以相互替换。这种模式使得算法可以独立于使用它们的客户端而变化。 策略设计模式包含三个主…

【MyBatis源码】transaction包JdbcTransaction和 ManagedTransaction源码分析

&#x1f3ae; 作者主页&#xff1a;点击 &#x1f381; 完整专栏和代码&#xff1a;点击 &#x1f3e1; 博客主页&#xff1a;点击 文章目录 事务概述事务接口及工厂TransactionFactory接口Transaction接口 JDBC事务JdbcTransactiongetConnection() JdbcTransactionFactory使用…

OverLeaf

\verb|acmart| \verb&#xff1a;这是一个 LaTeX 命令&#xff0c;用来创建 “verbatim”&#xff08;字面量&#xff09;文本。它会按照你输入的内容原样输出&#xff0c;不会解析其中的任何 LaTeX 命令或者特殊字符。|&#xff1a;这是定界符。\verb 命令需要一对定界符来标…

预训练模型与ChatGPT:自然语言处理的革新与前景

目录 一、ChatGPT整体背景认知 &#xff08;一&#xff09;ChatGPT引起关注的原因 &#xff08;二&#xff09;与其他公司的竞争情况 二、NLP学习范式的发展 &#xff08;一&#xff09;规则和机器学习时期 &#xff08;二&#xff09;基于神经网络的监督学习时期 &…

楼盘智能化的关键技术:数字孪生如何落地?

随着智慧城市的不断发展&#xff0c;数字孪生技术逐渐成为实现智慧楼盘管理和运营的核心技术之一。通过创建与现实楼盘一一对应的虚拟模型&#xff0c;数字孪生不仅能够提供更加全面、动态的楼盘信息展示&#xff0c;还能为楼盘的建设、管理和用户体验优化提供精准的数据支持和…

SQLServer 服务器只接受 TLS1.0,但是客户端给的是 TLS1.2

Caused by: javax.net.ssl.SSLHandshakeException: the server selected protocol version TLS10 is not accepted by client preferences [TLS12] 原因描述&#xff1a;SQLServer 服务器只接受 TLS1.0&#xff0c;但是客户端给的是 TLS1.2 解决方法如下&#xff1a; 打开文件…

C#与PLC通讯时,数据读取和写入浮点数,字节转换问题(ModbusTCP)

在与PLC进行通讯时&#xff0c;会发现一个问题&#xff0c;浮点数1.2接收过来后&#xff0c;居然变成了两个16位的整数。 经过一系列的分析&#xff0c;这是因为在PLC存储浮点数时32位&#xff0c;我们接收过来的数据会变成两个16位的高低字节&#xff0c;而且我们进行下发数据…

AD学习笔记·空白工程的创建

编写不易&#xff0c;禁止搬运&#xff0c;仅供学习&#xff0c;感谢理解 序言 本文参考B站&#xff0c;凡亿教育&#xff0c;连接放在最后。 创建工程文件 在使用AD这个软件的电路板设计中&#xff0c;有很多的地方跟嘉立创eda还是有不一样的地方&#xff0c;其中一个地方就…

折叠屏手机拐点:三星领跌,华为小米逆势增长

科技新知 原创作者丨依蔓 编辑丨蕨影 折叠屏手机不香了&#xff1f;显示器出货量罕见下滑&#xff0c;并预计 2025 年仍将持续下降。 近日&#xff0c;市场调查机构 DSCC报告称&#xff0c; 2019 年至 2023 年&#xff0c;折叠屏市场曾保持每年至少 40% 的高速增长。然而&…

基于Java Springboot哈尔滨中心医院微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

【人工智能-科普】图神经网络(GNN):与传统神经网络的区别与优势

文章目录 图神经网络(GNN):与传统神经网络的区别与优势什么是图神经网络?图的基本概念GNN的工作原理GNN与传统神经网络的不同1. 数据结构的不同2. 信息传递方式的不同3. 模型的可扩展性4. 局部与全局信息的结合GNN的应用领域总结图神经网络(GNN):与传统神经网络的区别与…

基于 LLamafactory 的异步API高效调用实现与速度对比

文章目录 背景摘要简介代码实现运行结果速度对比异步调用速度同步调用速度 背景 原先经常调用各家的闭源大模型的API&#xff0c;如果使用同步的方式调用&#xff0c;速度会很慢。为了加快 API 的调用速度&#xff0c;决定使用异步调用 API 的方式。 摘要 通过异步方式调用大…