【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)。
相关推荐
1104.北光c°7 小时前
滑动窗口HotKey探测机制:让你的缓存TTL更智能
java·开发语言·笔记·程序人生·算法·滑动窗口·hotkey
仰泳的熊猫11 小时前
题目2570:蓝桥杯2020年第十一届省赛真题-成绩分析
数据结构·c++·算法·蓝桥杯
似水明俊德14 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net
无极低码14 小时前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
软件算法开发15 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
superior tigre15 小时前
22 括号生成
算法·深度优先
腾阳16 小时前
99%的人忽视了这一点:活着本身就是人生的意义,别让抑郁和内耗成为你的枷锁!
经验分享·程序人生·职场和发展·跳槽·学习方法·媒体
不吃西红柿的8516 小时前
[职场] 内容运营求职简历范文 #笔记#职场发展
笔记·职场和发展·内容运营
liyang_83016 小时前
邦芒秘诀:职场高手都具备的三个特征
职场和发展