Given the root of a binary tree, return the number of univalue subtrees.
A univalue subtree means all nodes of the subtree have the same value.
Example 1:
Input: root = [5,1,5,5,5,null,5] Output: 4
Example 2:
Input: root = [] Output: 0
Example 3:
Input: root = [5,5,5,5,5,null,5] Output: 6
Constraints:
[0, 1000].[0, 99].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 approach to counting univalue subtrees means we will check every single subtree to see if it qualifies. We will look at each node in the tree and consider it as the root of a potential univalue subtree.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def count_univalue_subtrees(root):
univalue_subtree_count = 0
def is_univalue_subtree(root_node):
# If the node is None, it's a univalue subtree by definition
if root_node is None:
return True
# Recursively check if left and right subtrees are univalue
left_subtree_is_univalue = is_univalue_subtree(root_node.left)
right_subtree_is_univalue = is_univalue_subtree(root_node.right)
# If either subtree is not univalue, the entire subtree isn't
if not left_subtree_is_univalue or not right_subtree_is_univalue:
return False
# Check if the current node's value matches its children's values
if root_node.left and root_node.value != root_node.left.value:
return False
if root_node.right and root_node.value != root_node.right.value:
return False
return True
def traverse(root_node):
nonlocal univalue_subtree_count
if root_node is None:
return
# Check if the subtree rooted at the current node is univalue
if is_univalue_subtree(root_node):
# Increment the count if it's a univalue subtree
univalue_subtree_count += 1
# Recursively traverse the left and right subtrees
traverse(root_node.left)
traverse(root_node.right)
traverse(root)
return univalue_subtree_countThe most efficient way to count univalue subtrees is to work from the bottom up. For each node, we check if the subtree rooted at that node is univalue based on its children's properties. We use the results of the smaller subtrees to determine the larger subtrees, thus avoiding redundant calculations.
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 count_univalue_subtrees(root):
number_of_univalue_subtrees = 0
def is_univalue(node):
nonlocal number_of_univalue_subtrees
if not node:
return True
# Check univalueness of subtrees first.
is_left_univalue = is_univalue(node.left)
is_right_univalue = is_univalue(node.right)
if not is_left_univalue or not is_right_univalue:
return False
# If children exist, check if their values match the current node.
if node.left and node.left.val != node.val:
return False
if node.right and node.right.val != node.val:
return False
# If all checks pass, increment the counter.
number_of_univalue_subtrees += 1
return True
is_univalue(root)
return number_of_univalue_subtrees| Case | How to Handle |
|---|---|
| Null or empty tree | Return 0, as an empty tree has no subtrees (univalue or otherwise). |
| Single node tree | Return 1, as a single node is a univalue subtree by definition. |
| Tree with all identical values | The entire tree is a single univalue subtree, and every node within it is also a univalue subtree. |
| Highly skewed tree (e.g., all nodes on one side) | The recursive calls will correctly traverse the skewed tree to identify all univalue subtrees even if it may lead to higher call stack depth. |
| Tree with alternating values at each level | Each node will have a value different from its children preventing any subtree bigger than a single node from being univalue. |
| Tree with Integer.MAX_VALUE or Integer.MIN_VALUE as node values | Comparisons between node values and child values should still work correctly. |
| Large tree (potential stack overflow with recursion) | Consider using iterative approach with explicit stack for very large trees, to avoid stack overflow. |
| Tree with duplicate subtrees, some univalue, some not | The recursive algorithm correctly explores each subtree and determines univalue status independently, correctly counting only univalue ones. |