Last active
February 18, 2019 12:58
-
-
Save lopespm/3615fc28d08f9f63125894cb8e057b0f to your computer and use it in GitHub Desktop.
Returns the size of the largest binary subtree that is complete, in Python - variant for exercise 9.1 on EPI (Elements of Programming Interviews)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Returns the size of the largest binary subtree that is complete, in Python - variant for exercise 9.1 on EPI (Elements of Programming Interviews) | |
# The gist of the solution is to keep track of the subtree's current number of nodes, current height and maximum height until that point. | |
# With the current number of nodes and height, one can calculate the root's own number of nodes and height via its direct childs respective information, | |
# taking into to consideration the relation between the child heights and if they are perfect subtrees or not. | |
# Solution is O(n) time complexity and O(h) space complexity (function call stack corresponds from the root through the unique path to the current node) | |
from collections import namedtuple | |
class BTN(): | |
def __init__(self, data=None, left=None, right=None): | |
self.data = data | |
self.left = left | |
self.right = right | |
# number of nodes for a perfect tree of the given height | |
def max_nodes_per_height(height: int) -> int: | |
return 2**(height + 1) - 1 | |
def height_largest_complete_subtree(root: BTN) -> int: | |
CompleteInformation = namedtuple('CompleteInformation', ['height', 'num_nodes', 'max_height']) | |
def height_largest_complete_subtree_aux(root: BTN) -> CompleteInformation: | |
if (root is None): | |
return CompleteInformation(-1, 0, 0) | |
left_complete_info = height_largest_complete_subtree_aux(root.left) | |
right_complete_info = height_largest_complete_subtree_aux(root.right) | |
left_height = left_complete_info.height | |
right_height = right_complete_info.height | |
if (left_height == right_height): | |
if (left_complete_info.num_nodes == max_nodes_per_height(left_height)): | |
new_height = left_height + 1 | |
new_num_nodes = left_complete_info.num_nodes + right_complete_info.num_nodes + 1 | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
else: | |
new_height = left_height | |
new_num_nodes = max_nodes_per_height(left_height) | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
elif (left_height > right_height): | |
if (max_nodes_per_height(right_height) == right_complete_info.num_nodes): | |
new_height = right_height + 2 | |
new_num_nodes = min(left_complete_info.num_nodes, max_nodes_per_height(right_height + 1)) + right_complete_info.num_nodes + 1 | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
else: | |
new_height = right_height + 1 | |
new_num_nodes = max_nodes_per_height(right_height) + right_complete_info.num_nodes + 1 | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
elif (left_height < right_height): | |
if (left_complete_info.num_nodes == max_nodes_per_height(left_height)): | |
new_height = left_height + 1 | |
new_num_nodes = left_complete_info.num_nodes + max_nodes_per_height(left_height) + 1 | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
else: | |
new_height = left_height | |
new_num_nodes = (max_nodes_per_height(left_height - 1) * 2) + 1 | |
return CompleteInformation(new_height, | |
new_num_nodes, | |
max(new_height, max(left_complete_info.max_height, right_complete_info.max_height)) | |
) | |
return height_largest_complete_subtree_aux(root).max_height | |
tree = BTN('A', BTN('B', BTN('D', BTN('F')), BTN('E')), BTN('C')) | |
print(height_largest_complete_subtree(tree)) | |
tree = BTN('A', None, BTN('B')) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D', BTN('G', BTN('K'), BTN('L')), BTN('H')), BTN('E', BTN('I'), BTN('J'))) | |
right = BTN('C', BTN('F')) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D', BTN('G', BTN('K'), BTN('L')), BTN('H')), BTN('E', BTN('I'), BTN('J'))) | |
right = BTN('C', BTN('F'), BTN('CA')) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D', BTN('G', BTN('K'), BTN('L')), BTN('H')), BTN('E', BTN('I'), BTN('J'))) | |
right = BTN('C', BTN('F', BTN('CB'), BTN('CC')), BTN('CA', BTN('CD'), BTN('CE'))) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D', BTN('G', BTN('K'), BTN('L')), BTN('H')), BTN('E', BTN('I'), BTN('J'))) | |
right = BTN('C', BTN('F', BTN('CB'), BTN('CC')), BTN('CA', BTN('CD', BTN('CE')))) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D')) | |
right = BTN('C', BTN('E', BTN('G', BTN('K'), BTN('L')), BTN('H')), BTN('F', BTN('I'), BTN('J'))) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) | |
left = BTN('B', BTN('D')) | |
right = BTN('C', BTN('E', BTN('G')), BTN('F')) | |
tree = BTN('A', left, right) | |
print(height_largest_complete_subtree(tree)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment