【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)。
相关推荐
2301_800895102 小时前
hh的蓝桥杯每日一题--拔河
职场和发展·蓝桥杯
CCPC不拿奖不改名2 小时前
计算机网络:电脑访问网站的完整流程详解+面试习题
开发语言·python·学习·计算机网络·面试·职场和发展
寻星探路2 小时前
【算法专题】哈希表:从“两数之和”到“最长连续序列”的深度解析
java·数据结构·人工智能·python·算法·ai·散列表
!停2 小时前
C语言单链表
c语言·数据结构·算法
独自破碎E2 小时前
【队列】求二叉树的层序遍历
leetcode
闻缺陷则喜何志丹2 小时前
【回文 字符串】3677 统计二进制回文数字的数目|2223
c++·算法·字符串·力扣·回文
Tisfy3 小时前
LeetCode 0085.最大矩形:单调栈
算法·leetcode·题解·单调栈
mit6.8243 小时前
出入度|bfs|状压dp
算法
hweiyu003 小时前
强连通分量算法:Kosaraju算法
算法·深度优先