20251122树的直径、重心、中心

引入

树是一种特殊的图,因其看起来像一颗倒挂的树而得名。

树的定义为: n n n个点 n − 1 n-1 n−1条边的无向连通图。

树的直径

定义树上任意两点之间最长的简单路径为树的直径,一棵树可能有很多直径,如菊花图等。

DFS求法

在没有负边权的情况下,我们一般使用两次DFS求树的直径:

  1. 第一次DFS从任意位置出发,找到距离起点最远的点 x , x x,x x,x是一条直径的端点之一;
  2. 第二次DFS从点 x x x出发,找到距离点 x x x最远的点 y y y, x x x到 y y y的路径即为一条直径。

树形DP

当图中存在负边权时,无法使用DFS算法求解最长路径。此时应采用树形DP 方法:首先选取任意节点作为根节点,对于每个节点 x x x,计算其子树中以 x x x为顶点的最长路径。该路径长度等于 x x x向下的最长路径与次长路径之和。在DFS遍历过程中,只需维护这两个路径长度信息即可完成计算。

实现

cpp 复制代码
void dfs(int u,int fa){
  	d1[u]=d2[u]=0;     //d1是最长路,d2是次长路
  	for(int v:E[u]){
    	if(v==fa)continue;
    	dfs(v,u);
    	int dv=d1[v]+1;
    	if(dv>d1[u]){
    		d2[u]=d1[u];
     		d1[u]=dv; 
    	}else if(dv>d2[u]){
      		d2[u]=dv;
  		}
  	}
  	ans=max(ans,d1[u]+d2[u]);
}

树的重心

要确定树的重心,需选择一个根节点使其子树分布尽可能均匀。这里用最大子树的节点数来衡量均匀程度------该数值越小,分布越均匀。因此,使最大子树节点数最小的根节点即为树的重心。

性质

  1. 树的重心最多只有两个,若有两个一定相邻。
  2. 以重心作为根节点,根节点的最大子树节点数不会超过 n / 2 n/2 n/2
  3. 树上所有点到某个点的距离之和中,到重心的最小。
  4. 把两棵树用一条边连起来,形成的新的树的重心在原来两树重心之间的路径上。
  5. 在一颗树上添加一个叶子节点,重心最多向叶子节点移动一条边。

求法

以任意节点为根进行DFS遍历,可以计算每个节点的子树规模。具体而言:

  1. 向下递归时统计各子树节点数
  2. 向上部分的大小可通过公式n-size[cur]求得

实现

cpp 复制代码
void dfs(int x,int fa){
    for(int v:E[x]){
        if(v==fa)continue;
        dfs(v,x);
        sz[x]+=sz[v];
        mx[x]=max(mx[x],sz[v]);//向下子树大小
    }
    sz[x]++;
    mx[x]=max(mx[x],n-sz[x]);//向上子树大小
}

树的中心

树的中心指的是树中某个特殊节点,当以其为根时,能使得从该节点出发的最长路径长度达到最小。它具有以下关键特性:

  1. 树的中心数量不超过两个,且若存在两个中心则必定相邻
  2. 中心必然位于树的直径路径上
  3. 中心到任意节点的距离不超过树直径的一半
  4. 所有节点到其最远点的路径必然经过中心

求解步骤:

  1. 计算最长路径:采用深度优先搜索(DFS)算法,为每个节点计算其作为根时的最长路径和次长路径
  2. 计算外部路径:通过换根动态规划技术,计算每个节点在其子树之外的最长路径
  3. 确定中心节点:对每个节点求取其最长路径与外部路径的最大值,其中最小值对应的节点即为树的中心
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
vector<int> E[100005];
int n,dp1[100005],dp2[100005],p1[100005],p2[100005],up[100005];
void dfs(int x,int fa){
    for(int i=0;i<E[x].size();i++){
        int v=E[x][i];
        if(v==fa)continue;
        dfs(v,x);
        int s=dp1[v]+1;
        if(s>dp1[x]){
            dp2[x]=dp1[x];
            dp1[x]=s;
            p2[x]=p1[x];
            p1[x]=v;
        }else if(s>dp2[x]){
            dp2[x]=s;
            p2[x]=v;
        }
    }
}
void dfs1(int x,int fa){
    for(int i=0;i<E[x].size();i++){
        int v=E[x][i];
        if(v==fa)continue;
        if(v==p1[x]){
            up[v]=max(dp2[x],up[x])+1;
        }else{
            up[v]=max(dp1[x],up[x])+1;
        }
        dfs1(v,x);
    }
}
int main(){
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        E[u].push_back(v);
        E[v].push_back(u);
    }
    dfs(1,0);
    dfs1(1,0);
    int mn=INT_MAX;
    for(int i=1;i<=n;i++){
        mn=min(mn,max(dp1[i],up[i]));
    }
    for(int i=1;i<=n;i++){
        if(max(dp1[i],up[i])==mn){
            cout<<i<<" ";
        }
    }
    return 0;
}
相关推荐
AAA阿giao10 小时前
从树到楼梯:数据结构与算法的奇妙旅程
前端·javascript·数据结构·学习·算法·力扣·
量子炒饭大师11 小时前
Cyber骇客的树状逻辑数据——【初阶数据结构与算法】树
c语言·数据结构·c++·二叉树·
罗湖老棍子1 天前
最小函数值(minval)(信息学奥赛一本通- P1370)
数据结构·c++·算法··优先队列·
伟大的车尔尼1 天前
双指针题目:两数之和 IV - 输入二叉搜索树
二叉树··二叉搜索树·双指针
罗湖老棍子6 天前
【例3-3】医院设置(信息学奥赛一本通- P1338)
数据结构·c++·算法·
南莺莺15 天前
二叉排序树的创建和基本操作---C++实现
数据结构·c++·算法··二叉排序树
CQ_YM15 天前
数据结构之树
数据结构·算法·
大千AI助手1 个月前
多叉树:核心概念、算法实现与全领域应用
人工智能·算法·决策树·机器学习··多叉树·大千ai助手
奔跑吧邓邓子2 个月前
【C语言实战(71)】C语言进阶:树与图的奇妙数据之旅
c语言···开发实战