You are given the root
of a binary tree and an integer k
.
Return an integer denoting the size of the k<sup>th</sup>
largest perfect binary subtree*, or -1
if it doesn't exist.
A perfect binary tree is a tree where all leaves are on the same level, and every parent has two children.
For example:
Consider the following binary tree:
5
/ \
3 6
/ \ / \
5 2 5 7
/ \ / \
1 8 6 8
If k = 2
, the perfect binary subtrees (highlighted in black if we were to draw it) have sizes [3, 3, 1, 1, 1, 1, 1, 1]
. Therefore, the 2nd largest size is 3.
As another example, consider the binary tree:
1
/ \
2 3
/ \ / \
4 5 6 7
If k = 1
, the sizes of the perfect binary subtrees in non-increasing order are [7, 3, 3, 1, 1, 1, 1]
. The size of the largest perfect binary subtree is 7.
Finally, consider the binary tree:
1
/ \
2 3
/
4
If k = 3
, the sizes of the perfect binary subtrees in non-increasing order are [1, 1]
. There are fewer than 3 perfect binary subtrees, so return -1.
Write a function to solve this problem efficiently.
The most straightforward approach is to traverse the binary tree and, for each node, check if the subtree rooted at that node is a perfect binary tree. If it is, we calculate its size and store it. After traversing the entire tree, we sort the sizes of the perfect binary trees in non-increasing order and return the k-th largest size.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def is_perfect_binary_tree(root):
if not root:
return True, 0
def get_height(node):
if not node:
return 0
return 1 + get_height(node.left)
height = get_height(root)
def is_perfect(node, h, current_height=1):
if not node:
return h == current_height -1
if not node.left and not node.right:
return h == current_height
if not node.left or not node.right:
return False
return is_perfect(node.left, h, current_height+1) and is_perfect(node.right, h, current_height+1)
is_perfect_tree = is_perfect(root, height)
if is_perfect_tree:
size = 2**height -1
return True, size
else:
return False, 0
def kth_largest_perfect_subtree_naive(root, k):
sizes = []
def traverse(node):
if not node:
return
is_perfect, size = is_perfect_binary_tree(node)
if is_perfect:
sizes.append(size)
traverse(node.left)
traverse(node.right)
traverse(root)
sizes.sort(reverse=True)
if k <= len(sizes):
return sizes[k-1]
else:
return -1
To optimize, we can use a bottom-up approach. For each node, we determine if the subtree rooted at that node is a perfect binary tree based on the information from its children. We can calculate the height of the subtree and its size simultaneously. This avoids redundant calculations.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def kth_largest_perfect_subtree(root, k):
sizes = []
def traverse(node):
if not node:
return 0, True, 0 # height, is_perfect, size
left_height, left_perfect, left_size = traverse(node.left)
right_height, right_perfect, right_size = traverse(node.right)
if left_perfect and right_perfect and left_height == right_height:
height = left_height + 1
size = (1 << height) - 1 # 2**height - 1
sizes.append(size)
return height, True, size
else:
return -1, False, 0
traverse(root)
sizes.sort(reverse=True)
if k <= len(sizes):
return sizes[k - 1]
else:
return -1