【LeetCode 每日一题】865. 具有所有最深节点的最小子树——(解法一)自顶向下

Problem: 865. 具有所有最深节点的最小子树

文章目录

  • 整体思路
      • [1. 核心问题与功能](#1. 核心问题与功能)
      • [2. 算法与逻辑步骤](#2. 算法与逻辑步骤)
  • 完整代码
  • 时空复杂度
      • [1. 时间复杂度: O ( N 2 ) O(N^2) O(N2) (最坏情况)](#1. 时间复杂度: O ( N 2 ) O(N^2) O(N2) (最坏情况))
      • [2. 空间复杂度: O ( N ) O(N) O(N)](#2. 空间复杂度: O ( N ) O(N) O(N))

整体思路

1. 核心问题与功能

这段代码旨在找到二叉树中包含所有最深节点的最小子树 的根节点。

这等价于寻找所有最深叶子节点最近公共祖先 (LCA)

2. 算法与逻辑步骤

该解法采用了一种自顶向下的递归策略。

  • 辅助函数 getMaxDepth

    • 这是一个标准的递归函数,用于计算以当前节点为根的子树的最大深度(高度)。
    • 逻辑是:当前高度 = max(左子树高度, 右子树高度) + 1
  • 主递归函数 dfs(核心逻辑)

    • 从根节点开始,分别计算左子节点和右子节点所在子树的最大深度。
    • 情况 1:左深度 == 右深度
      • 这意味着最深的节点在左边也有,在右边也有。
      • 因此,当前节点就是涵盖左右两边所有最深节点的"分叉点",即我们要找的最近公共祖先。
    • 情况 2:左深度 < 右深度
      • 这意味着所有最深的节点都集中在右子树中。
      • 当前节点不是最小子树的根,我们需要放弃左边,进入右子树继续寻找。
    • 情况 3:左深度 > 右深度
      • 同理,意味着所有最深的节点都集中在左子树中。
      • 我们放弃右边,进入左子树继续寻找。

完整代码

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 辅助方法:计算当前节点及其子树的最大深度(高度)
    // 返回值表示从 node 到最远叶子节点的路径长度(节点数或边数逻辑一致)
    private int getMaxDepth(TreeNode node) {
        // 递归终止条件:空节点深度为 0
        if (node == null) {
            return 0;
        }
        // 当前深度 = 左右子树较大深度 + 1(加上当前节点自己)
        return Math.max(getMaxDepth(node.left), getMaxDepth(node.right)) + 1;
    }

    // 核心递归方法:寻找包含所有最深节点的子树根节点
    private TreeNode dfs(TreeNode node) {
        // 如果当前节点为空,直接返回 null(虽然在题目逻辑中通常不会走到这一步)
        if (node == null) {
            return null;
        }

        // 分别获取左子树和右子树的最大深度
        // 注意:这里会重复遍历子树节点来计算深度
        int leftDepth = getMaxDepth(node.left);
        int rightDepth = getMaxDepth(node.right);

        // 情况 1:左右子树深度相等
        // 说明最深层的节点分散在左右两边,当前 node 就是它们的最近公共祖先
        if (leftDepth == rightDepth) {
            return node;
        } 
        // 情况 2:右子树比左子树更深
        // 说明所有最深节点都在右子树里,目标节点肯定在右边,递归向右搜寻
        else if (leftDepth < rightDepth) {
            return dfs(node.right);
        } 
        // 情况 3:左子树比右子树更深
        // 说明所有最深节点都在左子树里,递归向左搜寻
        else {
            return dfs(node.left);
        }
    }

    public TreeNode subtreeWithAllDeepest(TreeNode root) {
        // 从根节点开始调用 dfs 逻辑
        return dfs(root);
    }
}

时空复杂度

1. 时间复杂度: O ( N 2 ) O(N^2) O(N2) (最坏情况)

  • 计算依据
    • 这是一个效率较低的实现。因为在 dfs 的每一步递归中,都会调用 getMaxDepth
    • getMaxDepth 本身的时间复杂度是 O ( M ) O(M) O(M),其中 M M M 是子树的节点数。
    • 最坏情况(树退化为链表)
      • 对于第 1 个节点,getMaxDepth 遍历 N − 1 N-1 N−1 个节点。
      • 对于第 2 个节点,getMaxDepth 遍历 N − 2 N-2 N−2 个节点。
      • 以此类推,总操作次数约为 N + ( N − 1 ) + . . . + 1 = O ( N 2 ) N + (N-1) + ... + 1 = O(N^2) N+(N−1)+...+1=O(N2)。
    • 最好情况(完全平衡二叉树)
      • 第一层遍历 N N N 个节点,第二层遍历 N / 2 N/2 N/2,第三层 N / 4 N/4 N/4...
      • 总和收敛于 O ( N ) O(N) O(N)。
    • 由于我们通常按最坏情况衡量,所以是 O ( N 2 ) O(N^2) O(N2)。

2. 空间复杂度: O ( N ) O(N) O(N)

  • 计算依据
    • 主要消耗在于递归调用栈。
    • dfs 函数和 getMaxDepth 函数都会使用栈空间。
    • 在最坏情况(退化为链表)下,递归深度等于树的高度 H H H,即 N N N。
    • 在平衡树情况下,递归深度为 log ⁡ N \log N logN。
  • 结论 : O ( N ) O(N) O(N)。
相关推荐
团子的二进制世界12 分钟前
G1垃圾收集器是如何工作的?
java·jvm·算法
吃杠碰小鸡16 分钟前
高中数学-数列-导数证明
前端·数学·算法
故事不长丨16 分钟前
C#线程同步:lock、Monitor、Mutex原理+用法+实战全解析
开发语言·算法·c#
long31617 分钟前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
近津薪荼18 分钟前
dfs专题4——二叉树的深搜(验证二叉搜索树)
c++·学习·算法·深度优先
熊文豪26 分钟前
探索CANN ops-nn:高性能哈希算子技术解读
算法·哈希算法·cann
熊猫_豆豆43 分钟前
YOLOP车道检测
人工智能·python·算法
艾莉丝努力练剑1 小时前
【Linux:文件】Ext系列文件系统(初阶)
大数据·linux·运维·服务器·c++·人工智能·算法
偷吃的耗子1 小时前
【CNN算法理解】:CNN平移不变性详解:数学原理与实例
人工智能·算法·cnn
dazzle2 小时前
机器学习算法原理与实践-入门(三):使用数学方法实现KNN
人工智能·算法·机器学习