leetcode
leetcode 1251 ~ 1300
二叉搜索子树的最大键值和

二叉搜索子树的最大键值和

难度:

标签:

题目描述

给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。

二叉搜索树的定义如下:

  • 任意节点的左子树中的键值都 小于 此节点的键值。
  • 任意节点的右子树中的键值都 大于 此节点的键值。
  • 任意节点的左子树和右子树都是二叉搜索树。

 

示例 1:

输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。

示例 2:

输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。

示例 3:

输入:root = [-4,-2,-5]
输出:0
解释:所有节点键值都为负数,和最大的二叉搜索树为空。

示例 4:

输入:root = [2,1,3]
输出:6

示例 5:

输入:root = [5,4,8,3,null,6,3]
输出:7

 

提示:

  • 每棵树有 140000 个节点。
  • 每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。

代码结果

运行时间: 173 ms, 内存: 34.7 MB


/*
题目思路:
1. 使用后序遍历遍历二叉树,以便处理每个节点时先处理其子节点。
2. 对于每个节点,计算其作为二叉搜索树的可能性,包括最大和。
3. 返回所有可能二叉搜索树的最大和。
使用 Java Stream 的方法。
*/

import java.util.Optional;
import java.util.stream.Stream;

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

class Solution {
    private class Result {
        int min, max, sum;
        boolean isBST;
        Result(int min, int max, int sum, boolean isBST) {
            this.min = min;
            this.max = max;
            this.sum = sum;
            this.isBST = isBST;
        }
    }

    private int maxSum = 0;

    public int maxSumBST(TreeNode root) {
        postOrder(root);
        return maxSum;
    }

    private Result postOrder(TreeNode node) {
        if (node == null) return new Result(Integer.MAX_VALUE, Integer.MIN_VALUE, 0, true);

        Result left = postOrder(node.left);
        Result right = postOrder(node.right);

        if (left.isBST && right.isBST && node.val > left.max && node.val < right.min) {
            int sum = node.val + left.sum + right.sum;
            maxSum = Math.max(maxSum, sum);
            return new Result(Math.min(node.val, left.min), Math.max(node.val, right.max), sum, true);
        } else {
            return new Result(0, 0, 0, false);
        }
    }

    public Stream<TreeNode> stream(TreeNode root) {
        if (root == null) return Stream.empty();
        return Stream.concat(Stream.of(root), Stream.concat(stream(root.left), stream(root.right)));
    }
}

解释

方法:

本题解采用深度优先搜索(DFS)方法来找出具有最大键值和的二叉搜索子树。对于每个节点,我们检查其是否能形成一个二叉搜索树(BST)。具体来说,DFS函数返回一个三元组:(子树的和, 子树的最小值, 子树的最大值)。若某个子树不是BST,我们使用一个非常小的标记值(float('-inf'))来标记其和,这样其父节点就不会错误地认为它可以形成BST。在递归过程中,我们更新全局变量ans来保持迄今为止找到的最大键值和。这种方法确保每个节点最多被访问一次。

时间复杂度:

O(n)

空间复杂度:

O(h),其中h是树的高度

代码细节讲解

🦆
为什么在判断子树是否为二叉搜索树时,需要同时考虑左子树的最大值和右子树的最小值?
在二叉搜索树(BST)中,任一节点的左子树中所有节点的值必须小于该节点的值,右子树中所有节点的值必须大于该节点的值。因此,在判断一个节点及其子树是否构成BST时,不仅需要检查该节点的直接子节点,也需要确认左子树中的最大值小于该节点的值,且右子树中的最小值大于该节点的值。这样可以确保整个子树都符合BST的性质。
🦆
DFS函数中使用float('-inf')来标记非BST子树的和,这种做法有无可能影响整体算法的正确性?例如在处理包含负数值节点的树时。
使用float('-inf')来标记非BST子树的和是为了在计算最大键值和时排除非BST子树。此做法不会影响算法的正确性,即使树中包含负数值节点。因为在计算过程中,任何包含非BST部分的和都被标记为float('-inf'),这个值在比较中总是小于任何正数或负数的和,因此不会被错误地考虑为可能的最大和。此外,只有完全符合BST条件的子树的和才会被用于更新最大和。
🦆
在递归过程中,为什么没有明显处理节点值为null的情况,这是否会影响函数的正确执行?
在递归处理中,如果节点值为null,即该节点不存在,通常表现为其父节点的left或right属性为null。在DFS函数的实现中,当一个节点不存在时(例如,root.left或root.right为null时),不会进入该节点的DFS递归调用。因此,不存在节点的情况已经隐含在递归调用的条件检查中,不需要额外的null值检查。这种处理方式简化了代码且不会影响函数的正确执行。
🦆
代码中对于叶子节点和非叶子节点的处理逻辑有所不同,能否详细解释为什么这样处理?
在二叉树中,叶子节点是没有子节点的节点。对于叶子节点,它本身就满足二叉搜索树的条件且其键值和就是节点本身的值。因此,对于叶子节点,可以直接返回其值作为键值和,并且更新全局的最大键值和。对于非叶子节点,需要检查其左右子树是否为BST,并确保左子树的最大值小于当前节点值,右子树的最小值大于当前节点值,才能将左右子树的和加上当前节点的值作为整个子树的和。这种区分处理是因为非叶子节点需要整合其子节点的信息来判断整个子树是否符合BST的条件。

相关问题