最小高度树(Minimum Height Trees)问题
字数 1694 2025-12-11 14:46:07
最小高度树(Minimum Height Trees)问题
问题描述
给定一个包含 n 个节点的树(即一个无向、连通且无环的图),以及一个从 0 到 n-1 的节点编号。树可以看作是以任意节点为根的有根树。我们的目标是找到所有可能的根节点中,使得整棵树的高度最小的那些根节点。树的高度定义为从根到最远叶子节点的最长路径上的边数。返回一个列表,包含所有最小高度树的根节点编号。
示例
- 输入:
n = 4, 边列表edges = [[1,0],[1,2],[1,3]] - 树的结构:
0 | 1 / \ 2 3 - 解释:以节点
1为根时,树的高度为1(最远叶子节点为0、2或3)。以其他节点为根时,高度至少为2。因此,最小高度树的根节点为[1]。
解题思路
此问题的核心在于 “树的中心” 概念。对于一棵树,其所有最小高度树的根节点恰好是树的 中心 或 中心的两个节点(如果直径路径的节点数为偶数,则中心是一个节点;如果为奇数,则中心是两个相邻节点)。因此,问题转化为:找到树的直径,然后确定其中心节点。
但更直接且高效的解法是 “逐层剥离叶子节点”(拓扑排序思想),直到剩余 1 个或 2 个节点,这些节点即为所求的根节点。为什么?因为最小高度树的根节点一定位于树的“最中间”,即距离所有叶子节点最远的点(或两个点)。通过不断删除叶子节点(度为 1 的节点),我们最终会收敛到中心。
算法步骤(BFS 层序遍历)
- 边界情况:如果
n <= 2,那么所有节点都是最小高度树的根节点,直接返回[0, ..., n-1]。 - 构建邻接表和度数组:
- 使用邻接表表示树。
- 记录每个节点的度(即相邻节点数)。
- 初始化队列:
- 将所有度为 1 的叶子节点加入队列。
- 层序剥离:
- 当剩余节点数
> 2时,进行以下操作:
a. 获取当前层的叶子节点数量size。
b. 对于队列中的每个叶子节点leaf:- 将其从图中“移除”(减少剩余节点数)。
- 遍历
leaf的每个邻居neighbor:- 将
neighbor的度减 1。 - 如果
neighbor的度变为 1,则将其加入队列(作为下一层的叶子节点)。
c. 处理完当前层后,剩余节点数更新。
- 将
- 当剩余节点数
- 返回结果:
- 当剩余节点数
<= 2时,队列中剩余的节点即为所有最小高度树的根节点。
- 当剩余节点数
举例说明
以示例 n = 6, edges = [[0,1],[0,2],[0,3],[3,4],[3,5]] 为例:
树结构:
1 2
\ /
0
|
3
/ \
4 5
步骤:
- 初始化:
- 度数组:
deg[0]=3, deg[1]=1, deg[2]=1, deg[3]=3, deg[4]=1, deg[5]=1 - 叶子节点(度为 1):
[1,2,4,5],加入队列。 - 剩余节点数
remaining = 6。
- 度数组:
- 第一层剥离(
remaining=6>2):- 处理叶子
1,2,4,5:- 移除
1:邻居0的度从 3 减为 2。 - 移除
2:邻居0的度从 2 减为 1(0变为新叶子,加入队列)。 - 移除
4:邻居3的度从 3 减为 2。 - 移除
5:邻居3的度从 2 减为 1(3变为新叶子,加入队列)。
- 移除
- 剩余节点:
[0,3],remaining=2。
- 处理叶子
- 停止剥离(
remaining<=2):- 队列中剩余节点为
[0,3],即为最小高度树的根节点。 - 验证:以
0或3为根,树的高度均为2。
- 队列中剩余节点为
时间复杂度与空间复杂度
- 时间复杂度:
O(n),每个节点和每条边只被处理一次。 - 空间复杂度:
O(n),用于存储邻接表和队列。
代码实现(Python 风格伪代码)
def findMinHeightTrees(n, edges):
if n <= 2:
return list(range(n))
# 构建邻接表和度数组
adj = [[] for _ in range(n)]
deg = [0] * n
for u, v in edges:
adj[u].append(v)
adj[v].append(u)
deg[u] += 1
deg[v] += 1
# 初始化叶子队列
leaves = deque([i for i in range(n) if deg[i] == 1])
remaining = n
# 层序剥离
while remaining > 2:
size = len(leaves)
remaining -= size
for _ in range(size):
leaf = leaves.popleft()
for neighbor in adj[leaf]:
deg[neighbor] -= 1
if deg[neighbor] == 1:
leaves.append(neighbor)
return list(leaves)
总结
最小高度树问题通过 “逐层剥离叶子节点” 的方法,高效地找到了树的中心节点。这个算法基于树的性质:中心节点是距离所有叶子节点最远的点,通过反复删除叶子节点,最终会收敛到中心。此方法避免了复杂的直径计算,直接得到所有可能的最小高度树根节点。