← Back to Course

Top Interview Questions for Trees

Tree Data Structures Overview

Tree Data Structures

Binary Trees, BSTs, and N-ary Trees

3. Base Template (Python Tree Traversals)

Below is a Python template for common tree traversals, using a simple TreeNode class.

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

# Pre-order Traversal (Root -> Left -> Right)
def preorder_traversal(root: TreeNode):
    if root is None: return []
    result = [root.val]
    result.extend(preorder_traversal(root.left))
    result.extend(preorder_traversal(root.right))
    return result

# In-order Traversal (Left -> Root -> Right)
def inorder_traversal(root: TreeNode):
    if root is None: return []
    result = []
    result.extend(inorder_traversal(root.left))
    result.append(root.val)
    result.extend(inorder_traversal(root.right))
    return result

# Post-order Traversal (Left -> Right -> Root)
def postorder_traversal(root: TreeNode):
    if root is None: return []
    result = []
    result.extend(postorder_traversal(root.left))
    result.extend(postorder_traversal(root.right))
    result.append(root.val)
    return result

# Level-order Traversal (BFS)
from collections import deque
def level_order_traversal(root: TreeNode):
    if root is None: return []
    result = []
    queue = deque([root])
    while queue:
        node = queue.popleft()
        result.append(node.val)
        if node.left: queue.append(node.left)
        if node.right: queue.append(node.right)
    return result
Dry Run Example (Pre-order for Tree: 1 / (2, 3)):
  1. preorder(1): result = [1]
  2. Call preorder(2): result = [2], returns [2]
  3. result.extend([2]) -> result = [1, 2]
  4. Call preorder(3): result = [3], returns [3]
  5. result.extend([3]) -> result = [1, 2, 3]
  6. Return [1, 2, 3]

4. Variants & Patterns (Tree Traversals)

DFS Traversal Variants

DFS with Path Tracking (Root-to-Leaf Paths Sum)

Modified Template: Add parameters for running sum and current path list in DFS. Recurse, on leaf check sum. Backtrack by removing node from path.

Example Problem: LeetCode 113. Path Sum II – Find all root-to-leaf paths where sum equals target.

Walkthrough (Tree [1,2,4,4,7,5,1], target=10):
  1. Root 1 (path [1], sum 1) -> L: 2 (path [1,2], sum 3) -> L: 4 (path [1,2,4], sum 7). Leaf, sum≠10. Backtrack.
  2. Node 2 -> R: 7 (path [1,2,7], sum 10). Leaf, sum==10. Record [1,2,7]. Backtrack.
  3. Root 1 -> R: 4 (path [1,4], sum 5) -> L: 5 (path [1,4,5], sum 10). Leaf, sum==10. Record [1,4,5]. Backtrack.
  4. Result: [[1,2,7],[1,4,5]].
def pathSum(root, target):
    res = []
    def dfs(node, cur_path, cur_sum):
        if not node: return
        cur_path.append(node.val)
        cur_sum += node.val
        if not node.left and not node.right and cur_sum == target:
            res.append(list(cur_path))
        else:
            dfs(node.left, cur_path, cur_sum)
            dfs(node.right, cur_path, cur_sum)
        cur_path.pop() # Backtrack
    dfs(root, [], 0)
    return res

DFS for Tree Transformation (Flatten Tree to List)

Modified Template: Use global pointer (or return value) for previously processed node. Traverse right subtree, then left (reverse pre-order). Rewire current node: node.right = prev, node.left = None. Update prev = node.

Example Problem: LeetCode 114. Flatten Binary Tree to Linked List – Flatten in-place to pre-order "linked list".

Tree Flattening Example
// C++ example
TreeNode* prev_node = nullptr; // Use a class member or global-like static
void flatten(TreeNode* root) {
    if (!root) return;
    flatten(root->right);
    flatten(root->left);
    root->right = prev_node;
    root->left = NULL;
    prev_node = root;
}

DFS Post-order for Lowest Common Ancestor (LCA)

Modified Template: DFS returns node pointer (target or LCA candidate). If node is p or q, return it. If left child returns p and right returns q (or vice-versa), current node is LCA. Else, propagate non-null child result.

Example Problem: LeetCode 236. Lowest Common Ancestor of a Binary Tree.

// Java example
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || root == p || root == q) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if (left != null && right != null) return root; // LCA found
    return (left != null) ? left : right;
}

Divide & Conquer DFS (Tree Construction)

Modified Template: Pick root from one traversal (e.g., pre-order[0]). Find root in inorder to split. Recursively build left/right subtrees from corresponding traversal segments. Use indices to avoid array copies.

Example Problem: LeetCode 105. Construct Tree from Preorder and Inorder Traversal.

def buildTree(preorder, inorder):
    inorder_idx_map = {val: i for i, val in enumerate(inorder)}
    preorder_idx = 0 # Global or nonlocal if Python 2/early 3

    def helper(in_left, in_right):
        nonlocal preorder_idx
        if in_left > in_right: return None
        
        root_val = preorder[preorder_idx]
        root = TreeNode(root_val)
        preorder_idx += 1
        
        inorder_root_idx = inorder_idx_map[root_val]
        
        root.left = helper(in_left, inorder_root_idx - 1)
        root.right = helper(inorder_root_idx + 1, in_right)
        return root
        
    return helper(0, len(inorder) - 1)

BFS Traversal Variants

Level Order Zigzag Traversal

Modified Template: BFS level by level. Maintain level counter or boolean. For each level, collect nodes. If odd level, reverse list before adding to results. Or use deque's appendleft for one direction.

Example Problem: LeetCode 103. Binary Tree Zigzag Level Order Traversal.

from collections import deque
def zigzagLevelOrder(root):
    if not root: return []
    result = []
    queue = deque([root])
    left_to_right = True
    while queue:
        level_nodes_deque = deque() # Use deque for efficient append/prepend
        level_size = len(queue)
        for _ in range(level_size):
            node = queue.popleft()
            if left_to_right: level_nodes_deque.append(node.val)
            else: level_nodes_deque.appendleft(node.val) # Prepend for R-L
            
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        result.append(list(level_nodes_deque))
        left_to_right = not left_to_right
    return result

Level Order Right-Side View

Modified Template: BFS level by level. For each level, record the last node's value.

Example Problem: LeetCode 199. Binary Tree Right Side View.

// C++ example
vector<int> rightSideView(TreeNode* root) {
    vector<int> view;
    if (!root) return view;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        int level_size = q.size();
        for(int i=0; i<level_size; ++i) {
            TreeNode* node = q.front(); q.pop();
            if (i == level_size - 1) view.push_back(node->val); // Last node
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
    }
    return view;
}

Connecting Level Siblings (Next Pointers)

Modified Template: BFS level by level. For each level, link each node to the next node in queue (its right sibling). Set last node's next to null.

Example Problem: LeetCode 116/117. Populating Next Right Pointers in Each Node.

// Python, assuming Node class has 'next' pointer
def connect(root):
    if not root: return None
    q = deque([root])
    while q:
        prev_node_in_level = None
        level_size = len(q)
        for _ in range(level_size):
            node = q.popleft()
            if prev_node_in_level: prev_node_in_level.next = node
            prev_node_in_level = node
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        if prev_node_in_level: prev_node_in_level.next = None # Last node in level
    return root

Inorder Traversal & BST Patterns

Validate BST (Inorder Check)

Modified Template: DFS inorder, keep track of previously seen value. If current value ≤ previous, BST property violated. Or, DFS with min/max bounds.

Example Problem: LeetCode 98. Validate Binary Search Tree.

// C++ example: DFS with min/max bounds
bool isValidBST_util(TreeNode* node, long minVal, long maxVal) {
    if (!node) return true;
    if (node->val <= minVal || node->val >= maxVal) return false;
    return isValidBST_util(node->left, minVal, node->val) &&
           isValidBST_util(node->right, node->val, maxVal);
}
bool isValidBST(TreeNode* root) {
    return isValidBST_util(root, LONG_MIN, LONG_MAX);
}

Kth Smallest Element (Inorder Ranking)

Modified Template: Inorder DFS with a counter. Decrement k (or increment count) when visiting node. Stop when counter hits k (or 0 if decrementing).

Example Problem: LeetCode 230. Kth Smallest Element in a BST.

// Java example: Iterative inorder
public int kthSmallest(TreeNode root, int k) {
    Stack<TreeNode> stack = new Stack<>();
    TreeNode curr = root;
    while (curr != null || !stack.isEmpty()) {
        while (curr != null) {
            stack.push(curr);
            curr = curr.left;
        }
        curr = stack.pop();
        if (--k == 0) { return curr.val; }
        curr = curr.right;
    }
    return -1; // Should not happen for valid k
}

BST to Greater Sum Tree (Reverse Inorder Accumulation)

Modified Template: DFS visiting right subtree, then node, then left (reverse inorder). Maintain a global running sum. At each node, add its value to sum, then update node's value to current sum.

Example Problem: LeetCode 538. Convert BST to Greater Tree.

# Python example
class Solution:
    total = 0
    def convertBST(self, root: TreeNode) -> TreeNode:
        if root:
            self.convertBST(root.right)
            self.total += root.val
            root.val = self.total
            self.convertBST(root.left)
        return root
    # Note: Reset self.total if calling multiple times on different trees with same Solution object.

5. Interactive Animation (Pre-order Traversal)

The animation below demonstrates Pre-order Depth-First Search (DFS) on a sample binary tree. Observe how the algorithm visits the current node, then its left subtree, then its right subtree. The "call stack" visually represents the recursion depth.

Sample Binary Tree for Traversal
Pre-order Traversal Trace (Root -> Left -> Right):
  1. Visit 10. Stack: [10]. Order: [10].
  2. Go Left. Visit 5. Stack: [10, 5]. Order: [10, 5].
  3. Go Left. Visit 3. Stack: [10, 5, 3]. Order: [10, 5, 3]. (3 is leaf, backtrack)
  4. Back to 5. Go Right. Visit 7. Stack: [10, 5, 7]. Order: [10, 5, 3, 7]. (7 is leaf, backtrack)
  5. Back to 5 (done with children). Back to 10.
  6. Go Right. Visit 15. Stack: [10, 15]. Order: [10, 5, 3, 7, 15].
  7. Go Left (None). Go Right. Visit 18. Stack: [10, 15, 18]. Order: [10, 5, 3, 7, 15, 18]. (18 is leaf, backtrack)
  8. Done. Final Order: [10, 5, 3, 7, 15, 18].
105371518
Call Stack: [], Order: []

Click 'Start Pre-order Animation'.

6. Time & Space Complexity

For an n-node tree:

  • Time Complexity (All Traversals): O(n) - each node visited once.
  • Space Complexity (DFS - Recursive): O(h) for call stack, where h is tree height. Worst case O(n) for skewed tree, best/average O(log n) for balanced tree.
  • Space Complexity (DFS - Iterative with Stack): O(h), similar to recursive.
  • Space Complexity (BFS - Level-order): O(w) where w is max width of tree. Worst case O(n) for a complete tree.

(Output list space is O(n) if storing all node values.)

7. Common Pitfalls

  • Incorrect or Missing Base Case: Not handling None nodes in recursion.
  • Off-by-One Errors: In height/depth calculations.
  • Infinite Recursion / Cycles: Usually not an issue for standard trees but be careful if modifying structure or treating as general graph.
  • Null Child Handling: Accessing properties of None children (e.g., node.left.val without checking node.left).
  • Using Wrong Traversal Approach: E.g., using DFS for shortest path when BFS is better.
  • Mistaking Binary Tree vs BST: Assuming BST properties for a general binary tree.
  • BST Edge Cases: Incorrect min/max bounds for validation, mishandling duplicates.
  • Not Accounting for Recursion Return Values: Forgetting to use or propagate results from recursive calls.
  • Edge Cases & Skewed Trees: Test with empty, single-node, and skewed trees.

8. Frequently Asked Tree Problems (LeetCode)

The following are some of the most frequently asked LeetCode problems involving trees:

Problem (LeetCode) Difficulty
Maximum Depth of Binary TreeEasy
Same TreeEasy
Symmetric TreeEasy
Balanced Binary TreeEasy
Invert Binary TreeEasy
Binary Tree Level Order TraversalMedium
Binary Tree Zigzag Level Order TraversalMedium
Binary Tree Right Side ViewMedium
Validate Binary Search TreeMedium
Subtree of Another TreeEasy
Lowest Common Ancestor of a Binary TreeMedium
Binary Tree Maximum Path SumHard
Serialize and Deserialize Binary TreeHard
Construct Binary Tree from Preorder and Inorder TraversalMedium
Path SumEasy
Kth Smallest Element in a BSTMedium

Solving these problems will solidify your understanding of tree fundamentals.

© Tree Data Structures Overview. Content adapted for educational purposes.

We use cookies to improve your experience. By clicking “Accept” you consent to the use of cookies. Read our Privacy Policy.