[HOT 100] 3203. 合并两棵树后的最小直径

1. 题目链接


3203. 合并两棵树后的最小直径 - 力扣(LeetCode)


2. 题目描述


给你两棵 无向 树,分别有 nm 个节点,节点编号分别为 0n - 10m - 1 。给你两个二维整数数组 edges1edges2 ,长度分别为 n - 1m - 1 ,其中 edges1[i] = [ai, bi] 表示在第一棵树中节点 aibi 之间有一条边,edges2[i] = [ui, vi] 表示在第二棵树中节点 uivi 之间有一条边。

你必须在第一棵树和第二棵树中分别选一个节点,并用一条边连接它们。

请你返回添加边后得到的树中,最小直径 为多少。

一棵树的 直径 指的是树中任意两个节点之间的最长路径长度。

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. 问题理解
    • 需要合并两棵树(通过连接任意两个节点)
    • 目标是使合并后的树直径最小
    • 树的直径定义为树中最长路径的边数
  2. 关键思路
    • 分别计算两棵树的原始直径
    • 合并后的最小直径取决于:
      • 两棵树各自的直径
      • 两棵树半径之和加1(连接后的新路径)
  3. 直径计算算法
    • 任意选择一个节点作为根
    • 通过DFS计算每个节点的最长子路径
    • 在递归过程中维护全局直径
  4. 合并策略
    • 最佳合并方式是连接两棵树的中心节点
    • 新直径 = 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))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值