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

news2024/12/23 18:58:03

目录

题目解析

算法原理

图的存储

算法实现 


题目解析

题目解析 

给定一颗树,树中包含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;
}

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

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

相关文章

STM32 进阶 定时器 2基本定时器 基本定时器中断案例:LED闪烁

基本定时器 基本定时器TIM6和TIM7各包含一个16位自动装载计数器&#xff0c;由各自的可编程预分频器驱动。 这2个定时器是互相独立的&#xff0c;不共享任何资源。 这个2个基本定时器只能向上计数&#xff0c;由于没有外部IO&#xff0c;所以只能计时&#xff0c;不能对外部…

51单片机(STC89C52RC版本)学习笔记(更新中...)

文章目录 参考资料1. 准备工作1.1 win10配置51单片机开发环境1.1 Ubuntu配置51单片机开发环境问题1&#xff1a;mcs51/8051.h依赖于mcs51/lint.h问题2&#xff1a;提示找不到头文件mcs51/8051.h 2. 认识51单片机2.1 STC89C52单片机2.2 管脚图2.3 原理图2.4 按键抖动2.5 头文件说…

USB 声卡全解析:提升音频体验的得力助手

在当今数字化的时代&#xff0c;音频领域的追求愈发多元。无论是热衷聆听高品质音乐的爱好者&#xff0c;还是在专业音频工作中精雕细琢的人士&#xff0c;亦或是在游戏世界里渴望极致音效沉浸的玩家&#xff0c;都始终在寻觅能让音频体验更上一层楼的妙法。而 USB 声卡&#x…

计算机的错误计算(一百七十四)

摘要 探讨 MATLAB 关于计算机的错误计算&#xff08;一百七十三&#xff09;中多项式的秦九韶&#xff08;或Horner&#xff09;形式的计算误差。 在计算机的错误计算&#xff08;一百七十三&#xff09;中&#xff0c;我们讨论了一个多项式的计算误差。本节探讨其对应秦九韶&…

Magento2如何创建CRUD Models

Mageno2 Model的创建不同于其他框架&#xff0c;需要3个不同目录层级的文件 例如需要为表hello_test创建model&#xff1a; 1、app/code/Hello/Test/Model/Test.php <?phpnamespace Hello\Test\Model;class Test extends \Magento\Framework\Model\AbstractModel {protec…

Visual Studio 2022 项目配置常用选项

作为一名C++开发者,经常需要配置第三方库,今天来跟大家截图一下,方便大家快速配置: 头文件包含目录: 或者: 库文件包含目录:

基于Vue实现的移动端手机商城项目 电商购物网站 成品源码

&#x1f4c2;文章目录 一、&#x1f4d4;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站演示 &#x1f4f8;部分截图 &#x1f3ac;视频演示 五、⚙️网站代码 &#x1f9f1;项目结构 &#x1f492;vue代码预览 六、&#x1f527;完整…

PHP使用RabbitMQ(正常连接与开启SSL验证后的连接)

代码中包含了PHP在一般情况下使用方法和RabbitMQ开启了SSL验证后的使用方法&#xff08;我这边消费队列是使用接口请求的方式&#xff0c;每次只从中取出一条&#xff09; 安装amqp扩展 PHP使用RabbitMQ前&#xff0c;需要安装amqp扩展&#xff0c;之前文章中介绍了Windows环…

uniapp h5 vue3 m3u8 和 mp4 外链视频播放

m3u8视频播放 使用mui-player 和hls.js。 安装npm install mui-player hls.js我的版本是"hls.js": "^1.5.17"和"mui-player": "^1.8.1"使用 页面标签&#xff1a; 引用&#xff1a; 点击目录播放视频&#xff1a; m3u8视频播放&a…

给el-table表头添加icon图标,以及鼠标移入icon时显示el-tooltip提示内容

在你的代码中&#xff0c;你已经正确地使用了 el-tooltip 组件来实现鼠标划过加号时显示提示信息。el-tooltip 组件的 content 属性设置了提示信息的内容&#xff0c;placement 属性设置了提示信息的位置。 你需要确保 el-tooltip 组件的 content 属性和 placement 属性设置正…

node.js实现分页,jwt鉴权机制,token,cookie和session的区别

文章目录 1. 分⻚功能2. jwt鉴权机制1.jwt是什么2.jwt的应用3.优缺点 3. cookie&#xff0c;token&#xff0c;session的对比 1. 分⻚功能 为什么要分页 如果数据量很⼤&#xff0c;⽐如⼏万条数据&#xff0c;放在⼀个⻚⾯显⽰的话显然不友好&#xff0c;这时候就需要采⽤分⻚…

大数据新视界 -- Hive 元数据管理:核心元数据的深度解析(上)(27 / 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

SpringBoot如何使用EasyExcel实现表格导出(简洁快速入门版本)

前言 前面给大家介绍了动态表头的导入&#xff0c;这篇文章给大家介绍如何实现导出 前面给大家介绍了动态表头的导入&#xff0c;我们了解了如何通过EasyExcel灵活地读取结构不固定的Excel文件。这次&#xff0c;我们将目光转向数据导出——即如何将数据以Excel文件的形式输出…

快速上手 RabbitMQ:使用 Docker 轻松搭建消息队列系统

在现代的分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;是实现异步通信、解耦系统组件、提高系统可扩展性和可靠性的重要工具。RabbitMQ 是一个广泛使用的开源消息代理软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xf…

ELK的Filebeat

目录 传送门前言一、概念1. 主要功能2. 架构3. 使用场景4. 模块5. 监控与管理 二、下载地址三、Linux下7.6.2版本安装filebeat.yml配置文件参考&#xff08;不要直接拷贝用&#xff09;多行匹配配置过滤配置最终配置&#xff08;一、多行匹配、直接读取日志文件、EFK方案&#…

UE5 像素流进行内网https证书创建

确定证书需求 内网 HTTPS 通信通常需要以下内容&#xff1a; 自签名证书&#xff08;适用于内网环境&#xff0c;不需要通过公开的证书颁发机构 CA&#xff09; 或者通过内部的企业 CA 签发的证书&#xff08;更安全&#xff09;。 生成自签名证书 使用工具&#xff08;如 Ope…

44页PDF | 信息化战略规划标准框架方法论与实施方法(限免下载)

一、前言 这份报告详细介绍了企业信息化战略规划的标准框架、方法论以及实施方法&#xff0c;强调了信息化规划应以业务战略和IT战略为驱动力&#xff0c;通过构筑企业架构&#xff08;EA&#xff09;来连接长期战略和信息化建设。报告提出了信息化规划原则&#xff0c;探讨了…

RNACOS:用Rust实现的Nacos服务

RNACOS是一个使用Rust语言开发的Nacos服务实现&#xff0c;它继承了Nacos的所有核心功能&#xff0c;并在此基础上进行了优化和改进。作为一个轻量级、快速、稳定且高性能的服务&#xff0c;RNACOS不仅包含了注册中心、配置中心和Web管理控制台的功能&#xff0c;还支持单机和集…

任务管理法宝:甘特图详解

在项目管理中&#xff0c;如何清晰、直观地展示项目的进度和任务分配&#xff1f; 甘特图作为一种经典的项目管理工具&#xff0c;提供了有效的解决方案。无论是团队合作还是个人项目管理&#xff0c;甘特图都能帮助你轻松追踪各项任务的进展。今天&#xff0c;我们将详细介绍…

RabbitMQ 客户端 连接、发送、接收处理消息

RabbitMQ 客户端 连接、发送、接收处理消息 一. RabbitMQ 的机制跟 Tcp、Udp、Http 这种还不太一样 RabbitMQ 服务&#xff0c;不是像其他服务器一样&#xff0c;负责逻辑处理&#xff0c;然后转发给客户端 而是所有客户端想要向 RabbitMQ服务发送消息&#xff0c; 第一步&a…