1. 什么是树的重心?
树的重心(Centroid of a Tree)是树形结构中的一个重要概念。对于一棵有 n 个节点的无根树,树的重心是指树中这样一个节点:如果以该节点为根,将树转换为有根树,那么所有子树中节点数最多的那个子树的节点数最小。
更形式化地定义:设树 T 有 n 个节点,对于任意节点 u,定义 size(u) 为以 u 为根的子树大小(节点数)。令 f(u) = max{size(v) | v 是 u 的子节点},则树的重心是使得 f(u) 最小的节点 u。
2. 重心的性质
树的重心具有以下几个重要性质:
- 存在性:每棵树至少有一个重心,最多有两个重心。
- 平衡性:以重心为根时,所有子树的大小都不超过 n/2。
- 唯一性条件:当 n 为奇数时,树有且仅有一个重心;当 n 为偶数时,可能有一个或两个重心。
- 递归性质:如果树有两个重心,那么这两个重心一定是相邻的。
- 最优性:在树分治算法中,以重心为分割点可以保证递归深度为 O(log n)。
3. 寻找重心的算法
寻找树的重心可以使用一次 DFS(深度优先搜索)完成,时间复杂度为 O(n)。
算法步骤:
- 任选一个节点作为根,进行 DFS。
- 对于每个节点 u,计算以 u 为根的子树大小 sizeu。
- 同时计算 f(u) = max(n - sizeu, max{sizev | v 是 u 的子节点})。
- 找到所有节点中 f(u) 最小的节点,即为重心。
4. 代码实现(C++)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
vector<int> graph[MAXN];
int size[MAXN], f[MAXN];
int n, centroid1 = -1, centroid2 = -1;
void dfs(int u, int parent) {
size[u] = 1;
int max_subtree = 0;
for (int v : graph[u]) {
if (v == parent) continue;
dfs(v, u);
size[u] += size[v];
max_subtree = max(max_subtree, size[v]);
}
max_subtree = max(max_subtree, n - size[u]);
f[u] = max_subtree;
}
int main() {
cin >> n;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
graph[u].push_back(v);
graph[v].push_back(u);
}
dfs(1, 0);
int min_f = *min_element(f + 1, f + n + 1);
for (int i = 1; i <= n; i++) {
if (f[i] == min_f) {
if (centroid1 == -1) centroid1 = i;
else centroid2 = i;
}
}
cout << "重心: " << centroid1;
if (centroid2 != -1) cout << " 和 " << centroid2;
cout << endl;
return 0;
}
5. 应用场景
树的重心在算法竞赛和实际应用中有着广泛用途:
- 树分治:点分治算法的核心就是每次选择重心作为分治点。
- 树的直径:重心与树的直径有密切联系,可以通过重心快速计算直径。
- 网络设计:在树形网络中,重心可以作为最优的服务器放置位置。
- 平衡分割:需要将树平衡分割时,重心是最佳分割点。
6. 例题解析
例题:给定一棵 n 个节点的树,求所有可能的重心。
分析:直接使用上述算法即可。注意当 n 为偶数时,可能存在两个重心,需要全部输出。
时间复杂度:O(n),空间复杂度 O(n)。
7. 总结
树的重心是树形结构中的一个重要概念,具有优秀的平衡性质。掌握重心的求法和性质,对于解决树相关算法问题(特别是树分治)非常有帮助。在实际编码中,只需一次 DFS 即可找到重心,代码简洁高效。