1. 问题背景与核心需求
最近在准备算法面试时遇到一道关于树结构的经典题目,题目大意是:给定一棵以节点1为根的无向树,节点编号为1到n。对于每个非根节点,我们需要找到它在树路径上第一个权值严格大于它的最近祖先节点,并在两者之间添加一条新边。最终要求计算所有节点到根节点的最短距离。
这个问题在实际应用中有很多场景,比如网络路由优化、社交网络关系强化等。理解并掌握这类树结构问题的解法,对于算法工程师来说是必备技能。下面我将详细解析这个问题,并给出Java、C++和Python三种语言的实现方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路分析
2.1 问题分解
首先我们需要明确几个关键点:
- 输入是一棵无向树,但以节点1为根,所以可以视为有根树
- 每个节点都有一个权值(题目中未明确说明,但根据上下文推断)
- 对于每个非根节点,需要找到其到根路径上第一个权值大于它的祖先节点
- 添加新边后,整个结构变成图,需要重新计算最短路径
2.2 核心算法选择
这个问题可以分解为两个主要部分:
- 对每个节点找到符合条件的祖先节点(最近更大祖先问题)
- 在添加新边后计算最短路径
对于第一部分,最直观的方法是暴力搜索,但时间复杂度会达到O(n²),对于大规模数据不可行。更优的解法是使用DFS遍历配合线段树或单调栈来维护当前路径上的节点信息,将时间复杂度降到O(n log n)。
对于第二部分,由于添加了新边后结构变为图,可以使用BFS来计算最短路径,因为所有边的权重可以视为相同(题目未明确说明则默认为1)。
3. 详细实现方案
3.1 数据结构设计
我们需要以下数据结构:
- 邻接表表示原始树结构
- 数组存储节点权值
- 线段树/单调栈维护当前DFS路径上的节点信息
- 队列用于BFS遍历
3.2 Java实现解析
java复制import java.util.*;
class Solution {
public int[] solve(int n, int[] parent, int[] value) {
// 构建树结构
List<List<Integer>> tree = new ArrayList<>();
for (int i = 0; i <= n; i++) {
tree.add(new ArrayList<>());
}
for (int i = 1; i < n; i++) {
tree.get(parent[i]).add(i+1);
tree.get(i+1).add(parent[i]);
}
// 寻找每个节点的最近更大祖先
int[] ancestor = new int[n+1];
Arrays.fill(ancestor, -1);
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // 根节点
// DFS遍历
boolean[] visited = new boolean[n+1];
visited[1] = true;
dfs(1, tree, v
