Given a binary tree, find the size of the largest subtree which is a Binary Search Tree (BST).
A valid BST is defined as follows:
Example 1:
Input: root = [10,5,15,1,8,null,7]Output: 3Explanation: The largest BST subtree is the one rooted at node 5. The nodes are 5, 1, and 8, giving a size of 3. The full tree is not a BST because node 7 in the right subtree of 15 is smaller than 10.
Example 2:
Input: root = [1,4,3,2]Output: 2Explanation:The largest BST subtree is the one rooted at node 3 with a left child of 2.
Example 3:
Input: root = [4,2,7,2,3,5,8]Output: 4Explanation:The largest BST subtree is the one rooted at node 7 with left child 5 and right child 8.
Constraints:
[0, 10⁴]
.-10⁴ <= Node.val <= 10⁴
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force way to find the largest Binary Search Tree (BST) subtree involves checking every possible subtree within the larger tree. We consider each node as the potential root of a BST and verify if the subtree rooted at that node actually follows the BST properties. We keep track of the largest valid BST subtree found.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def largest_bst_subtree_brute_force(root):
def is_bst(node, minimum_value, maximum_value):
if node is None:
return True
# Check BST property at current node
if node.data <= minimum_value or node.data >= maximum_value:
return False
# Recursively check left and right subtrees
return (is_bst(node.left, minimum_value, node.data) and\
is_bst(node.right, node.data, maximum_value))
def subtree_size(node):
if node is None:
return 0
return 1 + subtree_size(node.left) + subtree_size(node.right)
if root is None:
return 0
# Check if the subtree rooted at 'root' is a BST
if is_bst(root, float('-inf'), float('inf')):
return subtree_size(root)
# Explore left and right subtrees to find larger BSTs
left_size = largest_bst_subtree_brute_force(root.left)
right_size = largest_bst_subtree_brute_force(root.right)
# Return the maximum size among the subtrees
return max(left_size, right_size)
The most efficient way to solve this is to work from the bottom up. We check if each node is the root of a valid Binary Search Tree (BST) and keep track of the largest one we find so far. We do this without rechecking work, saving a lot of time.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def largest_bst_subtree(root):
largest_bst_size = 0
largest_bst_node = None
def is_bst(node, minimum_value, maximum_value):
if not node:
return True, 0
is_bst_left, size_left = is_bst(node.left, minimum_value, node.val)
is_bst_right, size_right = is_bst(node.right, node.val, maximum_value)
# If any subtree is not a BST, the whole tree is not a BST
if not is_bst_left or not is_bst_right or not (minimum_value <= node.val < maximum_value):
return False, 0
return True, size_left + size_right + 1
def traverse(node):
nonlocal largest_bst_size, largest_bst_node
if not node:
return
traverse(node.left)
traverse(node.right)
is_bst_node, current_size = is_bst(node, float('-inf'), float('inf'))
# Store the largest BST
if is_bst_node and current_size > largest_bst_size:
largest_bst_size = current_size
largest_bst_node = node
traverse(root)
return largest_bst_size
Case | How to Handle |
---|---|
Null root node | Return 0, as an empty tree has a BST size of 0. |
Single node tree | Return 1, as a single node is a valid BST of size 1. |
Skewed tree (left or right) | The recursive calls should still traverse the entire tree, correctly identifying subtrees and comparing sizes, though the performance will degrade to O(n). |
Tree with all identical values | Handle this by treating identical values as violating the BST property during the BST validation step. |
Tree with duplicate values that form valid BST subtrees | The BST validation check should correctly identify and count duplicate values if they satisfy the BST constraints of their respective subtrees. |
Integer overflow in subtree size calculations | Use appropriate data types (e.g., long) to store subtree sizes to avoid potential integer overflow issues. |
Very deep tree (potential stack overflow in recursive solutions) | Consider iterative approaches or tail-call optimization (if the language supports it) to avoid stack overflow errors. |
Root of the tree itself is a valid BST. | The algorithm should correctly identify this case and return the total number of nodes in the tree. |