Start learning free

LESSON 483 OF 671 · BINARY TREES

Maximum Sum BST in Binary Tree: II - Java Solution and Time Complexity Analysis

Practice O(n) Read the full explanation

You get the root of a binary tree. Return the largest sum of values in a subtree that is a binary search tree (BST).

A subtree is a node together with everything below it. It is a BST when this holds at each of its nodes:

  • every value in the node's left subtree is smaller than the node's value,
  • every value in its right subtree is larger.

An empty tree also counts as a BST, with a sum of 0. So the answer is never below 0.

The trees below are listed level by level, top to bottom and left to right. A missing child is written as null.

 

Example 1:

6 9 3 4 11 2 1 5

Input: root = [6,9,3,4,11,null,2,1,5]
Output: 30
Explanation: The blue subtree starts at 9.
It is a BST: 1 < 4 < 5 < 9 < 11.
Its sum is 9 + 4 + 11 + 1 + 5 = 30.
The whole tree is not a BST: 9 is left of 6 but larger.
The subtree from 3 down is not a BST: 2 is right of 3 but smaller.

Example 2:

-5 -8 4 -9 7

Input: root = [-5,-8,4,-9,7]
Output: 7
Explanation: The subtree from -8 down is a BST, but its sum is -10.
The whole tree is not a BST: 7 is left of -5 but larger.
The best BST is the single blue node 7.

Example 3:

Input: root = [-3,-6,-1]
Output: 0
Explanation: The whole tree is a BST, but every value is negative.
So any BST with nodes in it has a sum below 0.
The empty tree, with sum 0, is the best.

 

Constraints:

  • 1 to 25,000 nodes.
  • Each value is from -10,000 to 10,000.
  • The tree can be up to 10,000 levels deep.
STEP 1 OF 19
    Ctrl + Enter to check

    JAVA · BINARY TREES

    Max Sum Bst in Java, explained

    Understanding the Problem

    The core challenge of this problem is to identify the sub-trees within a binary tree that are valid Binary Search Trees (BSTs) and then calculate the sum of their nodes. The goal is to find the maximum sum among all such sub-trees.

    This problem is significant in various applications such as database indexing, memory management, and more, where BST properties are leveraged for efficient data retrieval and storage.

    Potential pitfalls include incorrectly identifying BSTs or missing edge cases where sub-trees might not be valid BSTs.

    Approach

    To solve this problem, we need to traverse the tree and check each sub-tree to see if it is a valid BST. A naive approach would involve checking each sub-tree individually, which would be highly inefficient.

    Instead, we can use a post-order traversal (left-right-root) to gather information about each sub-tree and determine if it forms a valid BST. This allows us to efficiently calculate the sum of nodes for valid BSTs and keep track of the maximum sum encountered.

    Naive Solution

    The naive solution involves checking each sub-tree individually to see if it is a valid BST and then calculating the sum of its nodes. This approach is not optimal due to its high time complexity.

    Optimized Solution

    The optimized solution uses a post-order traversal to gather information about each sub-tree. For each node, we check if its left and right sub-trees are valid BSTs and if the current node maintains the BST property. We also calculate the sum of nodes for valid BSTs and update the maximum sum encountered.

    Algorithm

    Here is a step-by-step breakdown of the optimized algorithm:

    1. Perform a post-order traversal of the tree.
    2. For each node, gather information about its left and right sub-trees (minimum value, maximum value, sum of nodes, and whether it is a valid BST).
    3. Check if the current node forms a valid BST with its left and right sub-trees.
    4. If it does, calculate the sum of nodes for this BST and update the maximum sum encountered.
    5. Return the maximum sum of all valid BSTs in the tree.

    Code Implementation

    // Definition for a binary tree node.
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }
    
    class Solution {
        // Class to store information about each subtree
        class SubTreeInfo {
            boolean isBST;
            int sum;
            int min;
            int max;
            
            SubTreeInfo(boolean isBST, int sum, int min, int max) {
                this.isBST = isBST;
                this.sum = sum;
                this.min = min;
                this.max = max;
            }
        }
        
        private int maxSum = 0;
        
        public int maxSumBST(TreeNode root) {
            postOrderTraversal(root);
            return maxSum;
        }
        
        private SubTreeInfo postOrderTraversal(TreeNode node) {
            if (node == null) {
                return new SubTreeInfo(true, 0, Integer.MAX_VALUE, Integer.MIN_VALUE);
            }
            
            SubTreeInfo leftInfo = postOrderTraversal(node.left);
            SubTreeInfo rightInfo = postOrderTraversal(node.right);
            
            if (leftInfo.isBST && rightInfo.isBST && node.val > leftInfo.max && node.val < rightInfo.min) {
                int sum = node.val + leftInfo.sum + rightInfo.sum;
                maxSum = Math.max(maxSum, sum);
                int min = Math.min(node.val, leftInfo.min);
                int max = Math.max(node.val, rightInfo.max);
                return new SubTreeInfo(true, sum, min, max);
            } else {
                return new SubTreeInfo(false, 0, 0, 0);
            }
        }
    }
    

    Complexity Analysis

    The time complexity of the optimized solution is O(n), where n is the number of nodes in the tree. This is because we perform a single post-order traversal of the tree.

    The space complexity is O(h), where h is the height of the tree. This is due to the recursive call stack used during the traversal.

    Edge Cases

    Potential edge cases include:

    • Empty tree: The output should be 0.
    • Tree with all negative values: The output should be 0, as the maximum sum of a valid BST would be 0.
    • Tree with only one node: The output should be the value of that node.

    These edge cases are handled by the algorithm as it checks for null nodes and correctly identifies valid BSTs.

    Testing

    To test the solution comprehensively, consider the following test cases:

    • Simple trees with a few nodes.
    • Large trees with thousands of nodes.
    • Trees with negative values.
    • Trees with only one node.

    Use testing frameworks such as JUnit to automate the testing process and ensure the solution works for all edge cases.

    Thinking and Problem-Solving Tips

    When approaching such problems, consider the following tips:

    • Break down the problem into smaller sub-problems.
    • Use tree traversal techniques to gather information about sub-trees.
    • Leverage properties of BSTs to optimize the solution.
    • Practice solving similar problems to improve problem-solving skills.

    Conclusion

    In this blog post, we discussed the problem of finding the maximum sum of all keys of any sub-tree which is also a Binary Search Tree (BST) in a given binary tree. We explored a naive solution and an optimized solution using post-order traversal. We also analyzed the time and space complexity of the solution and discussed potential edge cases and testing strategies.

    Understanding and solving such problems is crucial for developing efficient algorithms and improving problem-solving skills. We encourage readers to practice and explore further to deepen their understanding.

    Additional Resources

    For further reading and practice problems related to this topic, consider the following resources:

    Keep going with Java650+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free