1. 题目链接
3203. 合并两棵树后的最小直径 - 力扣(LeetCode)
2. 题目描述
给你两棵 无向 树,分别有 n
和 m
个节点,节点编号分别为 0
到 n - 1
和 0
到 m - 1
。给你两个二维整数数组 edges1
和 edges2
,长度分别为 n - 1
和 m - 1
,其中 edges1[i] = [ai, bi]
表示在第一棵树中节点 ai
和 bi
之间有一条边,edges2[i] = [ui, vi]
表示在第二棵树中节点 ui
和 vi
之间有一条边。
你必须在第一棵树和第二棵树中分别选一个节点,并用一条边连接它们。
请你返回添加边后得到的树中,最小直径 为多少。
一棵树的 直径 指的是树中任意两个节点之间的最长路径长度。
3. 题目示例
示例 1 :
输入:edges1 = [[0,1],[0,2],[0,3]], edges2 = [[0,1]]
输出:3
解释:
将第一棵树中的节点 0 与第二棵树中的任意节点连接,得到一棵直径为 3 的树。
示例 2 :
输入:edges1 = [[0,1],[0,2],[0,3],[2,4],[2,5],[3,6],[2,7]], edges2 = [[0,1],[0,2],[0,3],[2,4],[2,5],[3,6],[2,7]]
输出:5
解释:
将第一棵树中的节点 0 和第二棵树中的节点 0 连接,可以得到一棵直径为 5 的树。
4. 解题思路
- 问题理解:
- 需要合并两棵树(通过连接任意两个节点)
- 目标是使合并后的树直径最小
- 树的直径定义为树中最长路径的边数
- 关键思路:
- 分别计算两棵树的原始直径
- 合并后的最小直径取决于:
- 两棵树各自的直径
- 两棵树半径之和加1(连接后的新路径)
- 直径计算算法:
- 任意选择一个节点作为根
- 通过DFS计算每个节点的最长子路径
- 在递归过程中维护全局直径
- 合并策略:
- 最佳合并方式是连接两棵树的中心节点
- 新直径 = max(d1, d2, radius1 + radius2 + 1)
- 树的半径 = ceil(直径 / 2)
5. 题解代码
class Solution {
public int minimumDiameterAfterMerge(int[][] edges1, int[][] edges2) {
// 计算两棵树的原始直径
int d1 = diameter(edges1);
int d2 = diameter(edges2);
// 合并后的最小可能直径是以下三种情况的最大值:
// 1. 第一棵树的直径
// 2. 第二棵树的直径
// 3. 两棵树半径之和加1(连接后的新路径)
return Math.max(Math.max(d1, d2), (d1 + 1) / 2 + (d2 + 1) / 2 + 1);
}
private int res; // 用于记录直径的全局变量
// 计算树的直径
private int diameter(int[][] edges) {
// 构建邻接表表示的树结构(节点编号从0开始)
List<Integer>[] g = new ArrayList[edges.length + 1];
Arrays.setAll(g, i -> new ArrayList<>());
// 填充邻接表
for (int[] e : edges) {
int x = e[0];
int y = e[1];
g[x].add(y);
g[y].add(x);
}
res = 0; // 重置直径结果
dfs(0, -1, g); // 从任意节点(这里选0)开始DFS
return res;
}
// 深度优先搜索计算树的直径
private int dfs(int x, int fa, List<Integer>[] g) {
int maxLen = 0; // 记录当前节点的最长子路径
// 遍历所有邻接节点(除了父节点)
for (int y : g[x]) {
if (y != fa) {
// 递归计算子节点的路径长度(+1表示当前边)
int subLen = dfs(y, x, g) + 1;
// 更新全局直径(可能由两条子路径拼接)
res = Math.max(res, maxLen + subLen);
// 更新当前节点的最长子路径
maxLen = Math.max(maxLen, subLen);
}
}
return maxLen; // 返回当前节点的最长子路径长度
}
}
6. 复杂度分析
时间复杂度:O(n + m)
- 构建邻接表:O(n) 和 O(m)
- 两次DFS遍历:O(n) 和 O(m)
- n和m分别是两棵树的边数
空间复杂度:O(n + m)
- 邻接表存储空间
- 递归调用栈深度(最坏情况O(n)或O(m))