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

目录

题目解析

算法原理

图的存储

算法实现


题目解析

题目解析

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


单链表的实现

cpp 复制代码
#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;
}

邻接表的实现

cpp 复制代码
#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,表示该节点已经被遍历过了,反之未遍历过

cpp 复制代码
#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;
}
相关推荐
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码4 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7245 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活5 小时前
理解支持向量机
算法·机器学习·支持向量机
大山同学5 小时前
第三章线性判别函数(二)
线性代数·算法·机器学习