leetcode
leetcode 2651 ~ 2700
具有所有最深节点的最小子树

具有所有最深节点的最小子树

难度:

标签:

题目描述

Given the root of a binary tree, the depth of each node is the shortest distance to the root.

Return the smallest subtree such that it contains all the deepest nodes in the original tree.

A node is called the deepest if it has the largest depth possible among any node in the entire tree.

The subtree of a node is a tree consisting of that node, plus the set of all descendants of that node.

 

Example 1:

Input: root = [3,5,1,6,2,0,8,null,null,7,4]
Output: [2,7,4]
Explanation: We return the node with value 2, colored in yellow in the diagram.
The nodes coloured in blue are the deepest nodes of the tree.
Notice that nodes 5, 3 and 2 contain the deepest nodes in the tree but node 2 is the smallest subtree among them, so we return it.

Example 2:

Input: root = [1]
Output: [1]
Explanation: The root is the deepest node in the tree.

Example 3:

Input: root = [0,1,3,null,2]
Output: [2]
Explanation: The deepest node in the tree is 2, the valid subtrees are the subtrees of nodes 2, 1 and 0 but the subtree of node 2 is the smallest.

 

Constraints:

  • The number of nodes in the tree will be in the range [1, 500].
  • 0 <= Node.val <= 500
  • The values of the nodes in the tree are unique.

 

Note: This question is the same as 1123: https://leetcode.com/problems/lowest-common-ancestor-of-deepest-leaves/

代码结果

运行时间: 25 ms, 内存: 0.0 MB


/*
 * 思路:
 * 1. 使用深度优先搜索 (DFS) 计算每个节点的深度。
 * 2. 使用 Java Stream 找出所有最深节点。
 * 3. 找到这些最深节点的最近公共祖先 (LCA)。
 * 4. 返回这个公共祖先节点。
 */

import java.util.*;
import java.util.stream.*;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

class Solution {
    private Map<TreeNode, Integer> depthMap = new HashMap<>();
    private int maxDepth = 0;
    private TreeNode lca;

    public TreeNode subtreeWithAllDeepest(TreeNode root) {
        calculateDepth(root, 0);
        List<TreeNode> deepestNodes = depthMap.entrySet().stream()
            .filter(entry -> entry.getValue() == maxDepth)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
        lca = findLCA(root, new HashSet<>(deepestNodes));
        return lca;
    }

    private void calculateDepth(TreeNode node, int depth) {
        if (node == null) return;
        depthMap.put(node, depth);
        maxDepth = Math.max(maxDepth, depth);
        calculateDepth(node.left, depth + 1);
        calculateDepth(node.right, depth + 1);
    }

    private TreeNode findLCA(TreeNode node, Set<TreeNode> deepestNodes) {
        if (node == null || deepestNodes.contains(node)) return node;
        TreeNode left = findLCA(node.left, deepestNodes);
        TreeNode right = findLCA(node.right, deepestNodes);
        if (left != null && right != null) return node;
        return left != null ? left : right;
    }
}

解释

方法:

使用后序遍历的方式遍历二叉树。在遍历过程中,记录每个节点的高度,并比较左右子树的高度。如果左右子树高度相等,则当前节点就是最小子树的根节点;如果左子树高度大于右子树,则最小子树的根节点在左子树中;反之则在右子树中。最后返回最小子树的根节点。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
算法在处理只有一个节点的树(单节点树)时有何特殊考虑?是否还会执行比较左右子树高度的步骤?
在处理只有一个节点的树时,由于该节点既是根节点也是叶子节点,因此没有左子树和右子树。在递归函数`traverse`中,首先会检查当前节点是否存在,对于单节点树,该节点存在,然后对其左右子树进行递归调用。由于这两个子树都不存在,递归调用会立即返回`(None, 0)`。随后,该函数会比较左右子树的高度,都是0,因此相等。即使是单节点树,比较步骤仍会执行,但结果表明当前节点自身就是最小子树的根节点,因此返回`(当前节点, 1)`。
🦆
在递归函数`traverse`中,如果左子树和右子树的高度不相等,为何选择高度较大的子树的根节点作为返回的节点?
选择高度较大的子树的根节点作为返回的节点是因为,最深的节点(即最大深度的叶子节点)位于这棵更高的子树中。因此,包含所有最深节点的最小子树必然也在这个更高的子树中。返回较高子树的根节点确保了返回的子树包含了所有最深的叶子节点,这是解题核心所在。
🦆
算法中如何处理二叉树中的空节点?返回的高度为0是否有特殊的含义或者用途?
在算法中,对于空节点,递归函数`traverse`会返回`(None, 0)`。这里返回的高度0表示当前节点不存在任何子树,即没有高度。该值用于递归过程中比较节点的高度,以确定哪个子树是更高的,或者在两个子树高度相等时确认叶节点的高度。这种处理方式使得算法能够正确处理没有子节点的情况,同时简化了代码的复杂性,因为不需要编写额外的逻辑来专门处理空节点。

相关问题