# Interview Questions on Trees in Python

### Minimal Tree

#### We’re given a sorted array(ascending order) with unique integer elements. Write an algorithm to create a binary search tree with minimal height.

For the implementation of the solution to the given problem, we need to initialize the binary search tree class and overwrite the `__init__ `function as per our requirements. The method takes in three parameters – the data of the binary search tree, the `left `variable for the left subtree, and the `right `variable for the right subtree.

``````#Initializing the Binary Search Tree class
class BSTNode:
def __init__(self, data=None, left = None, right= None):
self.data = data
self.left = left
self.right = right``````

The `minimalTree` method will be used to create a binary search tree of minimum height using the given sorted array. It takes the sorted array as parameter.

The `minimalTree `method is based on the following algorithm:

• Create function and input the sorted array as parameter.
• If the length of the sorted array is 0, return `None`.
• If the length of the sorted array is 1, return the sole element of the array as the root node of the binary search tree.
• Create a variable mid to store the value of the mid point of the array under consideration, i.e., `mid = int(len(sortedArray)/2)`
• Recursively invoke the minimalTree method to add the elements to the left of the mid point in the left subtree.
• Recursively invoke the minimalTree method to add the elements to the right of the mid point in the right subtree.
• Finally, return the Binary Search Tree with all the elements inserted.
``````#Function to create a binary search tree of minimum height
def minimalTree(sortedArray):
if len(sortedArray) == 0:
return None
if len(sortedArray) == 1:
return BSTNode(sortedArray)
mid = int(len(sortedArray)/2)
left = minimalTree(sortedArray[:mid])
right = minimalTree(sortedArray[mid+1:])
return BSTNode(sortedArray[mid], left, right)

sortedArray = [1,2,3,4,5,6,7,8,9]
bst = minimalTree(sortedArray)``````

The binary Search Tree of minimal height has been created. However, the given solution won’t display the elements of the tree. To print the elements of the output tree, we can use Level Order Traversal that we studied before in the article, Binary Search Tree in Python.

### List of Depths

#### We’re given a binary search tree. Design an algorithm which creates a linked list of all the nodes at each depth. This means that if we have a binary search tree of depth D, then we’ll get D linked lists as output.

For the implementation of the solution to the given problem, we need to initialize the binary tree class and overwrite the `__init__ `function as per our requirements.

``````#Initializing the Binary Tree class
class BinaryTree:
def __init__(self, val):
self.val = val
self.left = None
self.right = None``````

Further, we initialize the linked list class and overwrite its `__init__ `function as well. Also, we include an `add `function to add nodes to the linked list and overwrite the `__str__ `function to display elements as desired.

``````#Initializing the Linked List class
def __init__(self, val):
self.val = val
self.next = None

if self.next == None:
else:
def __str__(self):
return "({val})".format(val = self.val) + str(self.next)``````

The `depth `method is used to return the depth of a given node. It takes this node as the parameter. If the left subtree of that node has a greater depth than the right subtree, we return that depth. Otherwise, the depth of the right subtree is returned.

``````#Initializing the depth class
def depth(tree):
if tree == None:
return 0
if tree.left == None  and tree.right == None:
return 1
else:
depthLeft = 1 + depth(tree.left)
depthRight = 1 + depth(tree.right)
if depthLeft > depthRight:
return depthLeft
else:
return depthRight``````

The `treeToLinkedList `method is a recursive function used to convert each depth of a tree into a linked list. The function takes three parameters as inputs – a node of the tree, a custom dictionary variable, and a variable to store the depth of the node.

The custom dictionary is used to store the elements of a specific depth. The key part of this dictionary holds the depth of the tree and the values part holds all the nodes of the tree at that particular depth.

The `treeToLinkedList `method is based on the following algorithm

• Create function and input the root node of the tree as parameter
• If the depth of the node is `None`, set the depth to the depth of the curret node.
• If the custom dictionary has no values for the current key “depth”, simply add the value of this node to the linked list.
• Otherwise, add all nodes of that depth to the custom dictioary. If depth equals 1(i.e, the nodes are leaf nodes), retun the custom dictionary.
• Recursively invoke the `treeToLinkedList `method for the left subtree of each node.
• Recursively invoke the `treeToLinkedList `method for the right subtree of each node.
• Finally, return the custom dictionary at the end of the function.
``````#Function to create linked list for nodes at each depth of the tree
def treeToLinkedList(tree, custDict = {}, d = None):
if d == None:
d = depth(tree)
if custDict.get(d) == None:
else:
if d == 1:
return custDict
if tree.left != None:
if tree.right != None:
return custDict

mainTree = BinaryTree(1)
two = BinaryTree(2)
three = BinaryTree(3)
four = BinaryTree(4)
five = BinaryTree(5)
six = BinaryTree(6)
seven = BinaryTree(7)

mainTree.left = two
mainTree.right = three
two.left = four
two.right = five
three.left = six
three.right = seven

print(custDict)
print(custDict)
print(custDict)

#Output
(1)None
(2)(3)None
(4)(5)(6)(7)None``````

### Check Balanced

#### Implement a function to check if a binary tree is balanced. A balanced tree is defined to be a tree such that the heights of the two subtrees of any node differ by more than one.

For the implementation of the solution to the given problem, we need to initialize the Binary Tree class and overwrite the `__init__ `function as per our requirements. We do this by declaring the node class of the binary tree.

``````#Initializing the Binary Tree class
class Node():
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right``````

The `isBalanced` function is given the root node of the tree as a parameter. This, in turn, calls the `isBalancedHelper` function which is responsible for returning a `True `output if the tree is balanced and `False `when it is not.

The `isBalancedHelper` function is based on the following algorithm:

• Declare the function and take the root of the tree as input.
• If the root node is `None`, return 0.
• Set a variable `leftHeight`. With this variable, invoke the function recursively for the left child of the current node. If `leftHeight `equals to -1, return -1.
• Set a variable `rightHeight`. With this variable, invoke the function recursively for the right child of the current node. If `rightHeight `equals to -1, return -1.
• If the absolute value of the difference between leftHeight and rightHeight is greater than 1, return -1.
• Return the following statement in the end of the algorithm: `return max(leftHeight, rightHeight) + 1`. If the max difference between any two subtrees is not greater than 1, a `True `value is returned.
``````#Function to check whether a given binary tree is balanced or not
def isBalancedHelper(root):
if root is None:
return 0
leftHeight = isBalancedHelper(root.left)
if leftHeight == -1:
return -1
rightHeight = isBalancedHelper(root.right)
if rightHeight == -1:
return -1
if abs(leftHeight-rightHeight)>1:
return -1

return max(leftHeight, rightHeight) + 1

def isBalanced(root):
return isBalancedHelper(root) > -1

N1 = Node("N1")
N2 = Node("N2")
N3 = Node("N3")
N4 = Node("N4")
N5 = Node("N5")
N6 = Node("N6")
N1.left = N2
N1.right = N3
N2.left = N4
N2.right = N5
N3.right = N6

print(isBalanced(N1))

#Output
True``````

### Validate BST

#### Implement a function to check if a binary tree is a binary search tree.

A Binary Tree is a Binary Search Tree if:

• The left subtree of a node contains nodes with values lesser than that node.
• The right subtree of a node contains nodes with values greater than that node.
• These conditions apply on all the nodes of the tree.

For the implementation of the solution to the given problem, we need to initialize the Binary Tree class and overwrite the `__init__ `function as per our requirements. We do this by declaring the node class of the binary tree.

``````#Initializing the Binary Tree class
class Node():
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right``````

The `isBSTValid `function is given the root node of the tree as a parameter. This, in turn, calls the `isBSTHelper` function which is responsible for returning a `True `output if the tree is Binary Search Tree and `False `when it is not.

The `isBSTHelper` function is based on the following algorithm:

• Declare the function and take the root of the tree as input, and two extra parameters – `minValue `and `maxValue`, initially set to infinity.
• If the root node is `None`, return `True`.
• Set a variable `val `and assign it the value of the current node. If the value is less than `minValue `or greater than `maxValue`, return `False`.
• Invoke the function recursively for the right child of the current node. If it fails, return `False`.
• Invoke the function recursively for the left child of the current node. If it fails, return False.
• Return `True `at the end of the function. This implies that all the nodes have been checked and thus the given tree is a binary search tree.
``````#Function to check whether a given binary tree is binary search tree or not
def BSTHelper(node, minValue = float('-inf'), maxValue = float('inf')):
if not node:
return True
val = node.val
if val <= minValue or val >= maxValue:
return False
if not BSTHelper(node.right, val, maxValue):
return False
if not BSTHelper(node.left, minValue, val):
return False

return True

def isBSTValid(root):
return BSTHelper(root)

root1 = Node(2)
root1.left = Node(1)
root1.right = Node(4)

print(isBSTValid(root1))

root2 = Node(4)
root2.left = Node(1)
root2.right = Node(3)

print(isBSTValid(root2))

#Output
True
False``````

### In-order Successor in BST

#### We are given a binary search tree. Write an algorithm to find the next node (i.e., the in-order successor) of a given node in the tree.

In a binary search tree, the in-order successor of a given node can be found in the mentioned two ways:

• If the right subtree of the node exists, then the successor lies in the right subtree.
• If the right subtree of the node does not exist, then the successor is one of the ancestors of the node.

For the implementation of the solution to the given problem, we need to initialize the Binary Tree class and overwrite the `__init__ `function as per our requirements. We do this by declaring the node class of the binary tree. Then, we define the `insertNode `class to input value in the tree.

``````#Initializing the Binary Tree class
class Node:
def __init__(self, key):
self.data = key
self.left = None
self.right = None

def insertNode(node, data):
if node is None:
return Node(data)
else:
if data <= node.data:
temp = insertNode(node.left, data)
node.left = temp
temp.parent = node
else:
temp = insertNode(node.right, data)
node.right = temp
temp.parent = node
return node``````

The `minValue `function is used to find the minimum value in a subtree. This is accomplished by traversing to the leftmost leaf element of the binary search tree. The `inOrderSuccessor `function takes two parameters – the root node of the binary search tree and the node whose in-order successor is to be found.

InOrder Traversal – 20 -> 30 -> 40 -> 50 -> 60 -> 70 -> 80 -> 90 -> 100

The `inOrderSuccessor `function is based on the following algorithm:

• Declare the function and take the root of the tree and a temporary current node as input.
• Let `n `be the node whose in-order successor is to be found.
• If the right child of `n `exists, return its value as the in-order successor.
• Otherwise, iterate over the tree and find the first ancestor for which `n `doesn’t lie in the right subtree. Return its value as the in-order successor.
``````#Function to return the in-order successor of a given node
def minValue(node):
current = node
while (current is not None):
if current.left is None:
break
current = current.left
return current

def inOrderSuccessor(root, n):
if n.right is not None:
return minValue(n.right)

p = n.parent
while p is not None:
if n != p.right:
break
n = p
p = p.parent
return p

root = None
root = insertNode(root, 4)
root = insertNode(root, 2)
root = insertNode(root, 8)
root = insertNode(root, 1)
root = insertNode(root, 3)
root = insertNode(root, 5)
root = insertNode(root, 9)

temp = root.left
successor = inOrderSuccessor(root, temp)

if successor is not None:
print("Inorder successor of %d is %d" %(temp.data, successor.data))
else:
print("Inorder successor does not exist")

#Output
Inorder successor of 2 is 3``````

### First Common Ancestor

#### We are given a binary tree. Write an algorithm to find the first common ancestor between two given nodes of the tree. Avoid storing additional nodes in any data structure

An ancestor of a node is any other node on the path from the node to the root

For the implementation of the solution to the given problem, we need to initialize the Binary Tree class and overwrite the `__init__ `function as per our requirements. We do this by declaring the node class of the binary tree.

``````#Initializing the Binary Tree class
class Node():
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right``````

Firstly, we create a helper function called `findNodeInTree` which takes in two parameters – the target node and the root node of the tree. This function returns `True `if the node is found and `False `if not.

``````#Helper function to find a target element
def findNodeInTree(target, rootNode):
if not rootNode:
return False
if target == rootNode:
return True
else:
return (findNodeInTree(target, rootNode.left) or findNodeInTree(target, rootNode.right))``````

Further, we define the `findFirstCommonAncestor `function which takes in three parameters – the two nodes under consideration and the root node of the tree. It is used to find the first common ancestor between two given nodes.

The `findFirstCommonAncestor` function is based on the following algorithm:

• Create the function and input the two nodes and the root node as parameter.
• Store the values of the `findNodeInTree` function for the nodes in the left subtree of the root. Name the variables `n1OnLeft `and `n2OnLeft `respectively.
• if the “exclusive or” of `n1OnLeft `and `n2OnLeft `is True(`n1OnLeft ^ n2OnLeft`), return the root node.
• Otherwise, recursively call the function for the right and left subtrees of the remaining nodes.
``````#Function to find the first common ancestor between two nodes
def findFirstCommonAncestor(n1, n2, root):
n1OnLeft = findNodeInTree(n1, root.left)
n2OnLeft = findNodeInTree(n2, root.left)

if n1OnLeft ^ n2OnLeft:
return root
else:
if n1OnLeft:
return findFirstCommonAncestor(n1, n2, root.left)
else:
return findFirstCommonAncestor(n1, n2, root.right)

node54 = Node(54)
node88 = Node(88, node54)
node35 = Node(35)
node22 = Node(22, node35, node88)
node33 = Node(33)
node90 = Node(90, None, node33)
node95 = Node(95)
node99 = Node(99, node90, node95)
node44 = Node(44, node22, node99)
node77 = Node(77)
rootNode = Node(55, node44, node77)

commonAncestor = findFirstCommonAncestor(node88, node33, rootNode)
print(commonAncestor.value)

#Output
44``````