Interview

15 Binary Tree Interview Questions and Answers

Prepare for your technical interview with this guide on binary trees, featuring common questions and detailed answers to enhance your understanding.

Binary trees are a fundamental data structure in computer science, widely used in various applications such as databases, file systems, and network routing algorithms. Their hierarchical nature allows for efficient searching, insertion, and deletion operations, making them a crucial concept for any software developer to master. Understanding binary trees is essential for tackling more complex data structures and algorithms.

This article provides a curated selection of binary tree interview questions designed to test and enhance your understanding of this critical topic. By working through these questions and their detailed answers, you will be better prepared to demonstrate your proficiency in binary trees during technical interviews.

Binary Tree Interview Questions and Answers

1. Write a function to perform pre-order traversal on a binary tree.

Pre-order traversal in a binary tree involves visiting the root node first, followed by the left subtree, and then the right subtree. This method is useful for creating a copy of the tree or for prefix notation of the tree nodes.

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def pre_order_traversal(root):
    if root:
        print(root.value, end=' ')
        pre_order_traversal(root.left)
        pre_order_traversal(root.right)

# Example usage:
# Constructing a simple binary tree
#        1
#       / \
#      2   3
#     / \
#    4   5

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

pre_order_traversal(root)
# Output: 1 2 4 5 3

2. Write a function to perform in-order traversal on a binary tree.

In-order traversal visits nodes in the order: left subtree, root, right subtree. This method is particularly useful for binary search trees (BST) because it visits nodes in ascending order.

class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

def in_order_traversal(root):
    result = []
    def traverse(node):
        if node:
            traverse(node.left)
            result.append(node.value)
            traverse(node.right)
    traverse(root)
    return result

# Example usage:
# Constructing a simple binary tree
#       1
#      / \
#     2   3
#    / \
#   4   5

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print(in_order_traversal(root))  # Output: [4, 2, 5, 1, 3]

3. Write a function to perform post-order traversal on a binary tree.

Post-order traversal visits nodes in the order: left subtree, right subtree, and then the root node. This method is useful for applications such as deleting a tree or evaluating expressions.

class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

def post_order_traversal(root):
    result = []
    
    def traverse(node):
        if node:
            traverse(node.left)
            traverse(node.right)
            result.append(node.value)
    
    traverse(root)
    return result

# Example usage:
# Constructing a simple binary tree
#       1
#      / \
#     2   3
#    / \
#   4   5

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print(post_order_traversal(root))  # Output: [4, 5, 2, 3, 1]

4. Write a function to find the height of a binary tree.

The height of a binary tree is the number of edges on the longest path from the root node to a leaf node. To find the height, use a recursive approach to calculate the height of the left and right subtrees and take the maximum of the two, adding one for the current node.

class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

def find_height(root):
    if root is None:
        return -1
    left_height = find_height(root.left)
    right_height = find_height(root.right)
    return max(left_height, right_height) + 1

# Example usage:
# Constructing a simple binary tree
#        1
#       / \
#      2   3
#     / \
#    4   5

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print(find_height(root))  # Output: 2

5. Write a function to find the lowest common ancestor (LCA) of two nodes in a BST.

The Lowest Common Ancestor (LCA) in a Binary Search Tree (BST) is the lowest node that has both given nodes as descendants. In a BST, this can be efficiently determined by leveraging the properties of the tree.

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

def lowestCommonAncestor(root, p, q):
    while root:
        if p.val < root.val and q.val < root.val:
            root = root.left
        elif p.val > root.val and q.val > root.val:
            root = root.right
        else:
            return root

6. Write a function to convert a sorted array into a balanced BST.

To convert a sorted array into a balanced BST, use a recursive approach. The middle element of the array will be the root, and the left and right halves will form the left and right subtrees, respectively.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def sortedArrayToBST(nums):
    if not nums:
        return None

    mid = len(nums) // 2
    root = TreeNode(nums[mid])
    root.left = sortedArrayToBST(nums[:mid])
    root.right = sortedArrayToBST(nums[mid+1:])
    
    return root

7. Write a function to perform level-order traversal (BFS) on a binary tree using a queue.

Level-order traversal, also known as Breadth-First Search (BFS), traverses a binary tree level by level from left to right. This can be implemented using a queue data structure.

from collections import deque

class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

def level_order_traversal(root):
    if not root:
        return []

    result = []
    queue = deque([root])

    while queue:
        level_size = len(queue)
        current_level = []

        for _ in range(level_size):
            node = queue.popleft()
            current_level.append(node.value)

            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

        result.append(current_level)

    return result

# Example usage:
# Constructing a simple binary tree
#         1
#        / \
#       2   3
#      / \   \
#     4   5   6

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.right = TreeNode(6)

print(level_order_traversal(root))
# Output: [[1], [2, 3], [4, 5, 6]]

8. Write functions to serialize and deserialize a binary tree.

Serialization and deserialization of a binary tree are essential for storing and transmitting tree structures. Serialization converts the tree into a string, making it easier to save or send. Deserialization reconstructs the tree from the string.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def serialize(root):
    def helper(node):
        if not node:
            return "None,"
        return str(node.val) + "," + helper(node.left) + helper(node.right)
    
    return helper(root)

def deserialize(data):
    def helper(nodes):
        val = next(nodes)
        if val == "None":
            return None
        node = TreeNode(int(val))
        node.left = helper(nodes)
        node.right = helper(nodes)
        return node
    
    return helper(iter(data.split(',')))

# Example usage:
# root = TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(4), TreeNode(5)))
# serialized = serialize(root)
# deserialized = deserialize(serialized)

9. Write a function to find the diameter of a binary tree.

The diameter of a binary tree is the length of the longest path between any two nodes. To find the diameter, calculate the height of the left and right subtrees for each node and keep track of the maximum diameter found.

class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

def diameter_of_binary_tree(root):
    def height_and_diameter(node):
        if not node:
            return 0, 0
        left_height, left_diameter = height_and_diameter(node.left)
        right_height, right_diameter = height_and_diameter(node.right)
        current_diameter = left_height + right_height
        max_diameter = max(current_diameter, left_diameter, right_diameter)
        return max(left_height, right_height) + 1, max_diameter

    return height_and_diameter(root)[1]

# Example usage:
# Constructing a binary tree
#        1
#       / \
#      2   3
#     / \
#    4   5
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print(diameter_of_binary_tree(root))  # Output: 3

10. Write a function to construct a binary tree from given inorder and preorder traversals.

To construct a binary tree from given inorder and preorder traversals, use the first element of the preorder traversal to identify the root. Find the root in the inorder traversal to determine the left and right subtrees, and repeat recursively.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def buildTree(preorder, inorder):
    if not preorder or not inorder:
        return None

    root_val = preorder.pop(0)
    root = TreeNode(root_val)
    inorder_index = inorder.index(root_val)

    root.left = buildTree(preorder, inorder[:inorder_index])
    root.right = buildTree(preorder, inorder[inorder_index + 1:])

    return root

11. Write a function to find the maximum path sum in a binary tree.

To find the maximum path sum in a binary tree, consider paths that can start and end at any node. Use a helper function that returns the maximum path sum including the current node and updates the global maximum path sum if the current path sum is higher.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def maxPathSum(root):
    def helper(node):
        nonlocal max_sum
        if not node:
            return 0
        
        left_max = max(helper(node.left), 0)
        right_max = max(helper(node.right), 0)
        
        current_max = node.val + left_max + right_max
        max_sum = max(max_sum, current_max)
        
        return node.val + max(left_max, right_max)
    
    max_sum = float('-inf')
    helper(root)
    return max_sum

# Example usage:
# root = TreeNode(1, TreeNode(2), TreeNode(3))
# print(maxPathSum(root))  # Output: 6

12. Explain what an AVL tree is and how it maintains balance.

An AVL tree is a self-balancing binary search tree. It maintains balance by ensuring that the height difference (balance factor) between the left and right subtrees of any node is at most one. If the balance factor becomes greater than one or less than negative one, the tree performs rotations to restore balance.

There are four types of rotations used to maintain balance in an AVL tree:

  • Right Rotation (RR): Applied when a left subtree is too tall.
  • Left Rotation (LL): Applied when a right subtree is too tall.
  • Left-Right Rotation (LR): Applied when a left subtree’s right child is too tall.
  • Right-Left Rotation (RL): Applied when a right subtree’s left child is too tall.

Here is a concise example demonstrating a right rotation:

class Node:
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None
        self.height = 1

def right_rotate(y):
    x = y.left
    T2 = x.right

    x.right = y
    y.left = T2

    y.height = max(height(y.left), height(y.right)) + 1
    x.height = max(height(x.left), height(x.right)) + 1

    return x

def height(node):
    if not node:
        return 0
    return node.height

13. Explain the concept of tree rotations and their importance in maintaining balance in AVL trees.

Tree rotations change the structure of a binary tree without affecting the in-order sequence of the elements. They are used in maintaining the balance of AVL trees, which are binary search trees with a balance condition that ensures the heights of the two child subtrees of any node differ by at most one.

There are four types of rotations:

  • Right Rotation (RR): Applied when a left-heavy subtree needs to be balanced.
  • Left Rotation (LL): Applied when a right-heavy subtree needs to be balanced.
  • Left-Right Rotation (LR): A combination of left and right rotations, used when a left-heavy subtree has a right-heavy child.
  • Right-Left Rotation (RL): A combination of right and left rotations, used when a right-heavy subtree has a left-heavy child.

Example of a Right Rotation (RR):

class TreeNode:
    def __init__(self, key):
        self.left = None
        self.right = None
        self.val = key

def right_rotate(y):
    x = y.left
    T2 = x.right

    # Perform rotation
    x.right = y
    y.left = T2

    return x

# Example usage
root = TreeNode(30)
root.left = TreeNode(20)
root.left.left = TreeNode(10)

# Perform right rotation
new_root = right_rotate(root)

14. Describe the process of deleting a node from a binary search tree (BST).

Deleting a node from a binary search tree (BST) involves three main scenarios:

1. Deleting a leaf node (a node with no children).
2. Deleting a node with one child.
3. Deleting a node with two children.

For each scenario, the process is as follows:

  • Deleting a leaf node: Simply remove the node from the tree.
  • Deleting a node with one child: Remove the node and replace it with its child.
  • Deleting a node with two children: Find the node’s in-order successor (the smallest node in the right subtree) or in-order predecessor (the largest node in the left subtree), replace the node’s value with the successor’s value, and then delete the successor node.

Here is a concise example to illustrate the deletion process:

class TreeNode:
    def __init__(self, key):
        self.left = None
        self.right = None
        self.val = key

def deleteNode(root, key):
    if not root:
        return root

    if key < root.val:
        root.left = deleteNode(root.left, key)
    elif key > root.val:
        root.right = deleteNode(root.right, key)
    else:
        if not root.left:
            return root.right
        elif not root.right:
            return root.left

        temp = minValueNode(root.right)
        root.val = temp.val
        root.right = deleteNode(root.right, temp.val)

    return root

def minValueNode(node):
    current = node
    while current.left:
        current = current.left
    return current

15. Discuss the advantages and disadvantages of using different types of binary trees (e.g., AVL, Red-Black, Splay Trees).

Binary trees are fundamental data structures in computer science, and various types of binary trees offer different advantages and disadvantages. Here, we will discuss AVL trees, Red-Black trees, and Splay trees.

AVL Trees:
Advantages:

  • AVL trees are self-balancing, ensuring that the height of the tree remains logarithmic relative to the number of nodes. This guarantees O(log n) time complexity for search, insertion, and deletion operations.
  • They are more rigidly balanced than Red-Black trees, which can lead to faster lookups in practice.

Disadvantages:

  • The strict balancing criteria require more rotations during insertion and deletion, which can make these operations slower compared to Red-Black trees.
  • The implementation is more complex due to the need to maintain balance factors and perform rotations.

Red-Black Trees:
Advantages:

  • Red-Black trees also provide O(log n) time complexity for search, insertion, and deletion operations.
  • They require fewer rotations to maintain balance compared to AVL trees, making insertions and deletions generally faster.

Disadvantages:

  • Red-Black trees are less rigidly balanced than AVL trees, which can result in slightly slower lookups.
  • The balancing rules are more complex, which can make the implementation challenging.

Splay Trees:
Advantages:

  • Splay trees are self-adjusting, meaning that recently accessed elements are moved closer to the root. This can lead to very fast access times for frequently accessed elements.
  • They are simpler to implement compared to AVL and Red-Black trees as they do not require maintaining balance factors or colors.

Disadvantages:

  • The worst-case time complexity for search, insertion, and deletion operations is O(n), although the amortized time complexity is O(log n).
  • They can become unbalanced with certain access patterns, leading to degraded performance.
Previous

10 Java Kafka Interview Questions and Answers

Back to Interview
Next

15 Unix Commands Interview Questions and Answers