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)。
- 对于第 1 个节点,
- 最好情况(完全平衡二叉树) :
- 第一层遍历 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)。