Given a binary tree root
, the task is to return the maximum sum of all keys of any sub-tree which is also a Binary Search Tree (BST).
Assume a BST is defined as follows:
Example 1:
Input: root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6] Output: 20 Explanation: Maximum sum in a valid Binary search tree is obtained in root node with key equal to 3.
Example 2:
Input: root = [4,3,null,1,2] Output: 2 Explanation: Maximum sum in a valid Binary search tree is obtained in a single root node with key equal to 2.
Example 3:
Input: root = [-4,-2,-5] Output: 0 Explanation: All values are negatives. Return an empty BST.
Example 4:
Input: root = [2,1,3] Output: 6
Example 5:
Input: root = [5,4,8,3,null,6,3] Output: 7
Constraints:
40000
nodes..[-4 * 10^4 , 4 * 10^4]
.The core challenge of this problem is to identify the largest sum of keys in any subtree that is a valid Binary Search Tree (BST). A BST is defined by the property that for any node, all nodes in its left subtree have keys less than the node's key, and all nodes in its right subtree have keys greater than the node's key. This problem has significant applications in data structures and algorithms, particularly in scenarios where hierarchical data needs to be optimized or analyzed.
To solve this problem, we need to traverse the tree and evaluate each subtree to determine if it is a BST and calculate its sum. A naive approach would involve checking each subtree individually, but this would be highly inefficient. Instead, we can use a post-order traversal to evaluate each node's subtrees and determine if they form a BST, while simultaneously calculating the sum of their keys.
The naive solution involves checking each subtree to see if it is a BST and then calculating its sum. This approach is not optimal because it involves redundant checks and calculations, leading to a high time complexity.
An optimized solution involves using a post-order traversal to evaluate each node's subtrees. During the traversal, we can keep track of the minimum and maximum values in the subtrees, as well as their sums. This allows us to determine if the current subtree is a BST and calculate its sum efficiently.
Here is a step-by-step breakdown of the optimized algorithm:
// Definition for a binary tree node.
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
function maxSumBST(root) {
let maxSum = 0;
function postOrder(node) {
if (!node) {
return { isBST: true, sum: 0, min: Infinity, max: -Infinity };
}
const left = postOrder(node.left);
const right = postOrder(node.right);
if (left.isBST && right.isBST && node.val > left.max && node.val < right.min) {
const sum = node.val + left.sum + right.sum;
maxSum = Math.max(maxSum, sum);
return {
isBST: true,
sum: sum,
min: Math.min(node.val, left.min),
max: Math.max(node.val, right.max)
};
} else {
return { isBST: false, sum: 0, min: -Infinity, max: Infinity };
}
}
postOrder(root);
return maxSum;
}
// Example usage:
const root = new TreeNode(1,
new TreeNode(4, new TreeNode(2), new TreeNode(4)),
new TreeNode(3, new TreeNode(2), new TreeNode(5, new TreeNode(4), new TreeNode(6)))
);
console.log(maxSumBST(root)); // Output: 20
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, due to the recursion stack.
Potential edge cases include:
These edge cases can be tested by providing appropriate input trees and verifying the output.
To test the solution comprehensively, we can use a variety of test cases, including simple and complex trees, trees with negative values, and edge cases. Here are some example test cases:
// Test case 1: Simple tree
const root1 = new TreeNode(2, new TreeNode(1), new TreeNode(3));
console.log(maxSumBST(root1)); // Output: 6
// Test case 2: Tree with negative values
const root2 = new TreeNode(-4, new TreeNode(-2), new TreeNode(-5));
console.log(maxSumBST(root2)); // Output: 0
// Test case 3: Complex tree
const root3 = new TreeNode(5,
new TreeNode(4, new TreeNode(3)),
new TreeNode(8, new TreeNode(6), new TreeNode(3))
);
console.log(maxSumBST(root3)); // Output: 7
When approaching such problems, it is important to:
In this blog post, we discussed the problem of finding the maximum sum of keys in any subtree of a binary tree that is also a BST. We explored a naive solution and an optimized solution using post-order traversal. We also provided a detailed code implementation, complexity analysis, and testing strategies. Understanding and solving such problems is crucial for improving algorithmic thinking and problem-solving skills.
For further reading and practice, consider the following resources: