Data Structures & Algorithms: Industry Edition
Unit 4: Trees
Binary Trees, BST, Traversals, LCA & Kth Element โ with real examples from Nykaa, Linux File System & MongoDB.
๐ข Real Projects | ๐ป 5 Lab Programs (Python + C) | ๐ 25 MCQs | ๐ฏ 3 Interview Questions
Industry Hook โ The Real-World Problem First
๐ The Nykaa Problem: 5 Million Products in a Hierarchy
Nykaa, India's largest beauty e-commerce platform, has over 5 million products organized into a deep category hierarchy:
Nykaa
/ | \
Beauty Fashion Wellness
/ \ |
Skincare Makeup Ethnic
/ \
Moisturizer Sunscreen
/ \
Under โน500 Premium
When a customer searches "moisturizers under โน500," the system must navigate this tree to reach the correct category โ that's tree traversal. When an admin adds a new sub-category, they insert a node โ that's tree insertion. When Nykaa needs to find the common category between "Sunscreen" and "Moisturizer," it finds "Skincare" โ that's the Lowest Common Ancestor (LCA).
The same tree structure powers your computer's file system (C:\ โ Users โ Documents), the Linux kernel's VFS, every database index (MongoDB uses B-Trees), and DOM manipulation in every web browser.
This is exactly the problem trees solve. Let's understand how.
Concept Explanation โ Theory, Earned
2.1 Binary Trees
Layer 1 โ Intuition
A tree is like a family tree โ one ancestor (root) at the top, and each person has at most two children (in a binary tree). You can't go "up" without following the parent pointer. A binary tree is the special case where every node has at most 2 children: left and right.
Layer 2 โ Visual
Binary Tree Structure
1 โ Root (Level 0)
/ \
2 3 โ Level 1
/ \ \
4 5 6 โ Level 2 (Leaves: 4, 5, 6)
Terminology:
Root = 1 (topmost node, no parent)
Leaf = 4, 5, 6 (no children)
Internal = 1, 2, 3 (at least one child)
Height = 2 (longest root-to-leaf path)
Depth(5) = 2 (edges from root to node 5)
Types of Binary Trees
| Type | Property | Example Use |
|---|---|---|
| Full Binary Tree | Every node has 0 or 2 children | Expression trees |
| Complete Binary Tree | All levels filled except possibly the last (filled left-to-right) | Binary heaps |
| Perfect Binary Tree | All internal nodes have 2 children, all leaves at same level | Theoretical ideal |
| Extended Binary Tree | Every node has exactly 0 or 2 children (same as Full) | Huffman coding trees |
Memory Representation
Linked Representation (Most Common)
Each node is a struct/class with 3 fields:
โโโโโโโโฌโโโโโโโฌโโโโโโโ
โ left โ data โ rightโ
โโโโฌโโโโดโโโโโโโดโโโโฌโโโ
โ โ
(left child) (right child)
Sequential (Array) Representation
For node at index i:
Left child = 2i + 1
Right child = 2i + 2
Parent = (i - 1) / 2
Index: 0 1 2 3 4 5
Value: [1] [2] [3] [4] [5] [6]
Used for: Binary heaps (complete trees). Wastes space for sparse trees.
2.2 Tree Traversals
The Three Traversal Orders
Tree: 1
/ \
2 3
/ \
4 5
In-order (Left, Root, Right): 4, 2, 5, 1, 3 โ Gives SORTED order for BST!
Pre-order (Root, Left, Right): 1, 2, 4, 5, 3 โ Used to COPY/SERIALIZE a tree
Post-order (Left, Right, Root): 4, 5, 2, 3, 1 โ Used to DELETE a tree safely
In-order traversal of a BST always gives sorted output. This is the BST's superpower. If you insert 50, 30, 70, 20, 40, 60, 80 into a BST and do an in-order traversal, you get: 20, 30, 40, 50, 60, 70, 80. Sorted! This is how databases retrieve records "in order" โ they traverse a B-Tree in-order.
2.3 Binary Search Tree (BST)
Layer 1 โ Intuition
A BST is like a dictionary organized by first letter. If you're looking for "Mango," you open to the middle. "Mango" > "J"? Go right half. "Mango" < "P"? Go left half. Each decision eliminates half the remaining pages โ exactly like binary search, but built into the tree structure itself.
Layer 2 โ BST Property
BST Property
For EVERY node:
โข All values in LEFT subtree < node's value
โข All values in RIGHT subtree > node's value
50
/ \
30 70 โ 30 < 50 < 70 โ
/ \ / \
20 40 60 80 โ 20 < 30 < 40 โ, 60 < 70 < 80 โ
Search for 40:
50 โ 40 < 50 โ go LEFT
30 โ 40 > 30 โ go RIGHT
40 โ FOUND! (3 comparisons for 7 nodes)
Layer 3 โ Complexity Table
| Operation | Average (Balanced) | Worst (Skewed) | Space |
|---|---|---|---|
| Search | O(log n) | O(n) | O(1) iterative / O(h) recursive |
| Insert | O(log n) | O(n) | Same |
| Delete | O(log n) | O(n) | Same |
| In-order traversal | O(n) | O(n) | O(h) stack |
| Find Min/Max | O(log n) | O(n) | O(1) |
If you insert elements [10, 20, 30, 40, 50] into a BST in that order, what does the tree look like? It becomes a right-skewed linked list! Search becomes O(n). This is why the ORDER of insertion matters, and why self-balancing trees (AVL, Red-Black) exist โ they guarantee O(log n) regardless of insertion order.
Lab Program Implementations
LAB PROGRAM 1: Create and Traverse Binary Tree Recursively
Python Implementation
Python
class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def inorder(root):
"""Left โ Root โ Right. Gives sorted order for BST."""
if root:
inorder(root.left)
print(root.val, end=" ")
inorder(root.right)
def preorder(root):
"""Root โ Left โ Right. Used to serialize/copy a tree."""
if root:
print(root.val, end=" ")
preorder(root.left)
preorder(root.right)
def postorder(root):
"""Left โ Right โ Root. Used to delete a tree safely."""
if root:
postorder(root.left)
postorder(root.right)
print(root.val, end=" ")
# Build 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: ", end=""); inorder(root); print()
print("Pre-order: ", end=""); preorder(root); print()
print("Post-order:", end=""); postorder(root); print()
C Implementation
C
#include <stdio.h>
#include <stdlib.h>
struct Node { int data; struct Node *left, *right; };
struct Node* newNode(int val) {
struct Node* n = (struct Node*)malloc(sizeof(struct Node));
n->data = val; n->left = n->right = NULL; return n;
}
void inorder(struct Node* r) { if(r){inorder(r->left);printf("%d ",r->data);inorder(r->right);} }
void preorder(struct Node* r) { if(r){printf("%d ",r->data);preorder(r->left);preorder(r->right);} }
void postorder(struct Node* r) { if(r){postorder(r->left);postorder(r->right);printf("%d ",r->data);} }
int main() {
struct Node* root = newNode(1);
root->left = newNode(2); root->right = newNode(3);
root->left->left = newNode(4); root->left->right = newNode(5);
printf("In: "); inorder(root); printf("\n");
printf("Pre: "); preorder(root); printf("\n");
printf("Post:"); postorder(root); printf("\n");
return 0;
}
LAB PROGRAM 2: Find All Paths from Root to Leaf
Python
def root_to_leaf_paths(root, path, all_paths):
"""Find every path from root to a leaf node.
Uses backtracking โ add current node, recurse, then remove."""
if not root:
return
path.append(root.val) # Add current node to path
if not root.left and not root.right: # Leaf โ record complete path
all_paths.append(list(path))
else:
root_to_leaf_paths(root.left, path, all_paths)
root_to_leaf_paths(root.right, path, all_paths)
path.pop() # Backtrack โ remove current node
# Tree: 1
# / \
# 2 3
# / \
# 4 5
paths = []
root_to_leaf_paths(root, [], paths)
for p in paths:
print(" โ ".join(map(str, p)))
C
void printPaths(struct Node* root, int path[], int pathLen) {
if(!root) return;
path[pathLen++] = root->data;
if(!root->left && !root->right) {
for(int i=0;i<pathLen;i++) printf("%d%s",path[i],i<pathLen-1?" -> ":"");
printf("\n");
}
printPaths(root->left, path, pathLen);
printPaths(root->right, path, pathLen);
}
This uses the backtracking pattern (append โ recurse โ pop). The same approach solves "find all paths in a maze," "all permutations," and "all subsets" โ fundamental interview patterns.
LAB PROGRAM 3: Lowest Common Ancestor (LCA) of Two Nodes
Python
def find_lca(root, p, q):
"""Find the Lowest Common Ancestor of nodes p and q.
LCA is the deepest node that is an ancestor of both p and q.
Industry: Nykaa finds the common category between two products.
Git uses LCA to find the merge-base of two branches.
Algorithm: If both p and q are in the left subtree, LCA is in left.
If both in right, LCA is in right. If they split, current node IS the LCA.
"""
if not root or root.val == p or root.val == q:
return root # Base: found p, q, or null
left = find_lca(root.left, p, q) # Search left subtree
right = find_lca(root.right, p, q) # Search right subtree
if left and right: # p and q are on different sides
return root # Current node IS the LCA!
return left if left else right # Both on same side โ pass up
# Tree: 1
# / \
# 2 3
# / \
# 4 5
result = find_lca(root, 4, 5)
print(f"LCA(4, 5) = {result.val}") # 2
result = find_lca(root, 4, 3)
print(f"LCA(4, 3) = {result.val}") # 1
C
struct Node* lca(struct Node* root, int p, int q) {
if(!root || root->data==p || root->data==q) return root;
struct Node* l = lca(root->left, p, q);
struct Node* r = lca(root->right, p, q);
if(l && r) return root;
return l ? l : r;
}
LAB PROGRAM 4: Insertion and Deletion in BST
Python
class BST:
def __init__(self):
self.root = None
def insert(self, val):
"""Insert value maintaining BST property. O(log n) avg."""
self.root = self._insert(self.root, val)
def _insert(self, node, val):
if not node:
return TreeNode(val) # Base: empty spot found
if val < node.val:
node.left = self._insert(node.left, val) # Go left
elif val > node.val:
node.right = self._insert(node.right, val) # Go right
# If val == node.val, skip (no duplicates)
return node
def delete(self, val):
"""Delete value from BST. Three cases:
1. Leaf: just remove
2. One child: replace with child
3. Two children: replace with in-order successor (min of right subtree)"""
self.root = self._delete(self.root, val)
def _delete(self, node, val):
if not node:
return node
if val < node.val:
node.left = self._delete(node.left, val)
elif val > node.val:
node.right = self._delete(node.right, val)
else: # Found the node to delete
if not node.left: # Case 1 & 2: no left child
return node.right
if not node.right: # Case 2: no right child
return node.left
# Case 3: two children โ find in-order successor
successor = self._find_min(node.right)
node.val = successor.val # Copy successor's value
node.right = self._delete(node.right, successor.val) # Delete successor
return node
def _find_min(self, node):
while node.left:
node = node.left
return node
def search(self, val):
return self._search(self.root, val)
def _search(self, node, val):
if not node or node.val == val:
return node
if val < node.val:
return self._search(node.left, val)
return self._search(node.right, val)
def inorder(self):
result = []
self._inorder(self.root, result)
return result
def _inorder(self, node, result):
if node:
self._inorder(node.left, result)
result.append(node.val)
self._inorder(node.right, result)
# === Driver ===
bst = BST()
for v in [50, 30, 70, 20, 40, 60, 80]:
bst.insert(v)
print("In-order:", bst.inorder())
print("Search 40:", "Found" if bst.search(40) else "Not found")
bst.delete(50)
print("After deleting 50:", bst.inorder())
C Implementation
C
struct Node* bstInsert(struct Node* root, int val) {
if(!root) return newNode(val);
if(val < root->data) root->left = bstInsert(root->left, val);
else if(val > root->data) root->right = bstInsert(root->right, val);
return root;
}
struct Node* findMin(struct Node* n) { while(n->left) n=n->left; return n; }
struct Node* bstDelete(struct Node* root, int val) {
if(!root) return root;
if(val < root->data) root->left = bstDelete(root->left, val);
else if(val > root->data) root->right = bstDelete(root->right, val);
else {
if(!root->left) { struct Node* t=root->right; free(root); return t; }
if(!root->right) { struct Node* t=root->left; free(root); return t; }
struct Node* s = findMin(root->right);
root->data = s->data;
root->right = bstDelete(root->right, s->data);
}
return root;
}
- Deleting a node with two children incorrectly: Students often try to just remove the node. You must replace it with its in-order successor (or predecessor) to maintain the BST property.
- Memory leak in C: When deleting a leaf, you must
free()it. Forgetting causes leaks in long-running database indexes. - Not returning the modified subtree: In recursive insert/delete, you must
return nodeto reconnect the tree. A common bug is_insert(node.left, val)withoutnode.left =.
LAB PROGRAM 5: Find Kth Smallest and Kth Largest Element in BST
Python
def kth_smallest(root, k):
"""In-order traversal gives sorted order. The k-th element visited
is the k-th smallest. O(h + k) time, O(h) space.
Industry: "Show the 5th cheapest product" on Nykaa/Flipkart."""
stack = []
current = root
count = 0
while stack or current:
while current: # Go to leftmost node
stack.append(current)
current = current.left
current = stack.pop() # Visit node
count += 1
if count == k:
return current.val # Found k-th smallest!
current = current.right # Move to right subtree
return None # k > number of nodes
def kth_largest(root, k):
"""Reverse in-order (Right, Root, Left) gives descending order.
The k-th element visited is the k-th largest."""
stack = []
current = root
count = 0
while stack or current:
while current: # Go to rightmost node
stack.append(current)
current = current.right
current = stack.pop()
count += 1
if count == k:
return current.val
current = current.left
return None
# BST: [20, 30, 40, 50, 60, 70, 80]
print(f"3rd smallest: {kth_smallest(bst.root, 3)}")
print(f"2nd largest: {kth_largest(bst.root, 2)}")
This iterative in-order approach using an explicit stack is the Morris Traversal alternative. It's preferred in interviews because it avoids the recursion limit. The key insight: in-order traversal of a BST = sorted order. So "Kth smallest" is simply "visit K nodes in-order." Time: O(H + K) where H = height.
Industry Case Studies
Case Study 1: Nykaa โ Product Category Hierarchy
Case Study 2: MongoDB โ B-Tree Indexes
Case Study 3: Linux โ Virtual File System (VFS) Tree
/home/user/code/main.py) must be fast.dentry (directory entry) structures. Each directory is a node whose children are its files and subdirectories. The dentry cache (dcache) keeps recently accessed path components in memory./home/user/code/main.py = 4 tree traversals. Dcache hit rate: ~99% โ most lookups don't touch disk at all.ls, cd, and open() call traverses a tree. The file system IS a tree. Your Lab Program 2 (root-to-leaf paths) is literally how find / -name "*.py" works.Chapter Project โ "Build It Yourself"
๐ File System Simulator with Folder Hierarchy
Pitch: Build a mini Windows Explorer / Linux terminal that lets you create folders, navigate with cd, list with ls, and find files โ all using a tree.
Problem Statement
You are a systems programming intern. Build a file system simulator using an N-ary tree where each node represents a file or directory with name, type (file/dir), and children.
Steps
- Step 1 โ Tree Node: Each node has
name,is_dir, andchildren[](list of child nodes). Root ="/". - Step 2 โ
mkdir: Add a child directory node to the current directory (tree insertion from Lab 4). - Step 3 โ
cd path: Navigate by following the tree path.cd ..goes to parent (needs parent pointer). - Step 4 โ
ls: List all children of current node (direct children traversal). - Step 5 โ
find name: Search entire subtree using pre-order traversal (Lab Program 1). - Step 6 โ
pwd: Print root-to-current path (Lab Program 2).
Extension Challenges
- โญ Easy: Add file sizes and compute directory sizes recursively
- โญโญ Medium: Implement
rm -r(recursive delete using post-order traversal) - โญโญโญ Hard: Implement
cpandmvcommands (subtree copy/move)
Interview Corner โ "Crack It at Google/Amazon"
Q1: Maximum Depth of Binary Tree โญ [University + Interview]
Python
def max_depth(root):
"""Height of tree = max depth of any leaf.
Base case: empty tree has depth 0.
Recursive: max(left height, right height) + 1."""
if not root:
return 0
return max(max_depth(root.left), max_depth(root.right)) + 1
Time: O(n), Space: O(h). Visits every node exactly once.
Follow-up: (1) Find minimum depth (shortest root-to-leaf). (2) Check if tree is balanced (left and right heights differ by at most 1).
Q2: Validate BST โญโญ [Amazon + Google]
Python
def is_valid_bst(root, min_val=float('-inf'), max_val=float('inf')):
"""Check if tree satisfies BST property.
Key insight: each node has a VALID RANGE, not just local comparison.
Node must be > all ancestors in its left lineage, < all in right."""
if not root:
return True
if root.val <= min_val or root.val >= max_val:
return False # Out of valid range!
return (is_valid_bst(root.left, min_val, root.val) and
is_valid_bst(root.right, root.val, max_val))
Students often check only node.left.val < node.val < node.right.val. This misses cases where a deep-left node violates the root's constraint. Example: root=10, root.right=15, root.right.left=6. Locally 6 < 15 is fine, but 6 < 10 violates the BST property (6 should be > 10 since it's in the right subtree of 10).
Q3: Level-Order Traversal (BFS) โญโญ [GATE + Interview]
Python
from collections import deque
def level_order(root):
"""BFS on a tree โ visit level by level using a queue."""
if not root: return []
result, queue = [], deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(level)
return result
# Output for our tree: [[1], [2, 3], [4, 5]]
Time: O(n), Space: O(w) where w = max width. Uses a queue โ connecting stacks/queues (Unit 3) to trees.
MCQ Assessment Bank โ 25 Questions
Level 1 โ Recall โญ (5 Questions)
In-order traversal visits nodes in which order?
- Root, Left, Right
- Left, Root, Right
- Left, Right, Root
- Right, Root, Left
๐ In-order: L-Root-R. Pre-order: Root-L-R. Post-order: L-R-Root.
โ (a) Pre-order. (c) Post-order. (d) Reverse in-order.
The BST property states that for every node:
- Left child > right child
- All left subtree values < node < all right subtree values
- Parent > both children
- All nodes are at the same level
๐ The BST property applies to the ENTIRE subtree, not just immediate children.
โ (a) Reversed. (c) That's a max-heap. (d) That's a perfect tree.
A binary tree where every node has 0 or 2 children is called:
- Complete binary tree
- Full binary tree
- Perfect binary tree
- Balanced binary tree
๐ Full = 0 or 2 children. Complete = all levels filled left-to-right. Perfect = full + all leaves at same level.
โ Others have different properties.
Maximum number of nodes in a binary tree of height h:
- 2h
- 2h + 1
- 2^(h+1) - 1
- hยฒ
๐ A perfect binary tree of height h has 1 + 2 + 4 + ... + 2^h = 2^(h+1) - 1 nodes. Height 2: 1+2+4 = 7 = 2ยณ-1.
โ Others are wrong formulas.
In array representation of a binary tree, the left child of node at index i is at:
- 2i
- 2i + 1
- i/2
- i + 1
๐ For 0-indexed arrays: left = 2i+1, right = 2i+2, parent = (i-1)/2. This is used in binary heaps.
โ (a) That's 1-indexed. (c) Parent formula. (d) Just the next element.
Level 2 โ Conceptual โญโญ (6 Questions)
In-order traversal of a BST always gives:
- Random order
- Sorted ascending order
- Sorted descending order
- Level order
๐ BST property guarantees: left < root < right. In-order (L-Root-R) visits smallest first, then root, then largest subtree. Result: ascending order.
โ (c) Reverse in-order gives descending.
Why does BST search degrade to O(n) when elements are inserted in sorted order?
- BSTs can't handle sorted data
- The tree becomes skewed (each node has only one child) โ effectively a linked list
- Sorted data causes memory overflow
- Hash collisions occur
๐ Inserting [10,20,30,40,50] creates: 10โ20โ30โ40โ50 (right-skewed). Height = n instead of log n. Search traverses all n nodes.
โ (a) BSTs can handle sorted data, just inefficiently. (c)(d) Unrelated.
To check if a binary tree is a valid BST, it's sufficient to verify that for every node, node.left.val < node.val < node.right.val.
- True โ this is the BST definition
- False โ you must check against the entire subtree range, not just immediate children
- True โ parent-child comparison is enough
- False โ BST validation is impossible
๐ Trap! Consider: root=10, right=15, right.left=6. Locally: 6<15 โ. But 6 is in root's RIGHT subtree and 6<10 โ BST violation! You must pass min/max bounds through recursion.
โ (a)(c) The local check misses ancestor violations.
Post-order traversal is used to delete a tree because:
- It visits the root first
- It processes children before the parent โ so you don't lose references to children
- It's the fastest traversal
- It doesn't use recursion
๐ If you delete the root first (pre-order), you lose access to both subtrees. Post-order deletes all children first, then the parent โ safe deletion order.
โ (a) Pre-order visits root first. (c)(d) Wrong.
When deleting a BST node with two children, we replace it with its:
- Left child
- Right child
- In-order successor (smallest in right subtree) or in-order predecessor
- Parent node
๐ The in-order successor is the smallest value greater than the deleted node. It maintains BST property because it's > everything in the left subtree and < everything else in the right subtree.
โ (a)(b) Would break BST property. (d) Doesn't maintain structure.
Level-order traversal of a tree uses which data structure?
- Stack
- Queue
- Array
- Heap
๐ BFS processes nodes level-by-level. A queue (FIFO) ensures nodes are visited in the order they were discovered โ parent's children are visited before grandchildren.
โ (a) Stack gives DFS (pre-order/post-order).
Level 3 โ Application โญโญ (8 Questions)
Nykaa's category tree has 5 million products and height 7. Maximum comparisons to find any product's category:
- 5,000,000
- 7
- 23 (logโ 5M)
- 50
๐ In a tree of height h, the maximum path from root to any node is h edges = h comparisons. With height 7, at most 7 comparisons regardless of total products.
โ (a) Linear search. (c) That's BST search on a balanced tree of 5M nodes, not height 7.
Pre-order: 1 2 4 5 3 6. In-order: 4 2 5 1 3 6. The tree is:
- 1 is root, 2 is left child, 3 is right child, 4/5 are children of 2, 6 is child of 3
- Can't determine
- 1 is root, 3 is left, 2 is right
- Multiple trees possible
๐ Pre-order first element (1) is root. In in-order, elements left of 1 (4,2,5) are left subtree, elements right (3,6) are right subtree. Recurse to build the full tree.
โ Pre+In uniquely determine a binary tree.
The minimum height of a BST containing n nodes is:
- n - 1 (right-skewed)
- โlogโ nโ
- logโ n + 1
- n / 2
๐ Trap: (a) is the MAXIMUM height (worst case). The MINIMUM height occurs when the tree is perfectly balanced. A complete binary tree of n nodes has height โlogโ nโ.
โ (a) Maximum height. (c)(d) Wrong formulas.
To find the Kth smallest in a BST of n nodes, the optimal time is:
- O(n) โ traverse entire tree
- O(h + k) โ go to leftmost, then visit k nodes in-order
- O(k log n)
- O(1)
๐ Traverse to the leftmost node (O(h)), then visit k nodes in-order (O(k)). Total: O(h + k). For a balanced tree: O(log n + k).
โ (a) Too slow. (d) Can't do constant time without augmented BST.
What is the space complexity of recursive in-order traversal?
- O(1)
- O(n)
- O(h) where h is tree height
- O(n log n)
๐ The recursion depth equals the tree height. Each call adds a frame to the call stack. For balanced trees: O(log n). For skewed: O(n). Generalized: O(h).
โ (a) Recursion uses stack space. (b) Only if skewed.
LCA of nodes 4 and 5 in the tree (root=1, left=2(children 4,5), right=3) is:
- 1
- 2
- 4
- 5
๐ Node 2 is the deepest node that is an ancestor of both 4 and 5. Node 1 is also a common ancestor but not the LOWEST one.
โ (a) Common ancestor but not lowest.
A BST is used instead of a sorted array when:
- Only search is needed
- Frequent insertions and deletions along with searches
- Data is accessed by index
- Data fits in cache
๐ Sorted array: O(log n) search but O(n) insert/delete (shifting). BST: O(log n) for all three operations. Use BST when data is dynamic (frequent changes).
โ (a) Sorted array is better for search-only. (c) Array is better for index access.
Which traversal is used to create a copy of a binary tree?
- In-order
- Pre-order
- Post-order
- Level-order
๐ Pre-order visits root first, then children. To copy: create root first, then recursively copy left and right subtrees. You can't create children before the parent.
โ (a)(c) Visit root after children โ can't create parent after children.
Level 4 โ Analysis โญโญโญ (6 Questions)
MongoDB's B-Tree index with branching factor 500 needs how many disk reads to search 125 million records?
- 27 (logโ 125M)
- 3 (logโ โโ 125M)
- 125,000,000
- 7
๐ B-Tree with branching factor b has height โ log_b(n). logโ โโ(125M) = log(125M)/log(500) โ 8.1/2.7 โ 3. Each level = one disk read. BST would need 27 reads. B-Tree: 9x fewer disk accesses.
โ (a) BST height, not B-Tree.
A balanced BST always has height exactly logโ(n).
- True
- False โ it has height โlogโ(n)โ for complete trees, but different balanced trees (AVL, Red-Black) allow slightly different heights
- True โ by definition of balanced
- False โ balanced trees have O(1) height
๐ "Balanced" doesn't mean "perfect." An AVL tree guarantees height โค 1.44ยทlogโ(n). A Red-Black tree guarantees โค 2ยทlogโ(n). Both are O(log n) but not exactly logโ(n). Only a perfect binary tree has height exactly โlogโ(n)โ.
โ The exact height depends on the balancing scheme.
Given only post-order and pre-order traversals, can you uniquely reconstruct the binary tree?
- Yes โ two traversals are always sufficient
- No โ you need in-order + one of the others
- Yes โ but only for BSTs
- No โ three traversals are needed
๐ Pre+Post can't distinguish left-only vs right-only children. Example: pre=[1,2], post=[2,1] โ is 2 the left or right child of 1? In-order resolves this ambiguity.
โ (a) Pre+Post is ambiguous. (c) BSTs can be reconstructed from pre-order alone.
For an LRU cache, which combination gives O(1) for both get and put?
- BST alone
- Hash Map + Doubly Linked List
- Array + BST
- Two stacks
๐ Hash map: O(1) lookup. DLL: O(1) move-to-front and remove-from-back. BST gives O(log n) โ too slow for cache operations that happen millions of times per second.
โ (a) O(log n). (c)(d) Can't achieve O(1) for both.
A complete binary tree is best stored in:
- Linked representation (pointers)
- Array representation (no wasted space)
- Hash table
- Stack
๐ A complete binary tree has no gaps โ every array position is used. Array gives O(1) parent/child access via index formulas. This is why binary heaps use arrays. Sparse trees waste array space.
โ (a) Works but uses extra pointer space.
The number of distinct BSTs that can be formed with n distinct keys is:
- n!
- 2โฟ
- Catalan number C(n) = (2n)! / ((n+1)! ร n!)
- nยฒ
๐ For n=3: C(3) = 5 distinct BSTs. For n=4: C(4) = 14. The Catalan number appears in many combinatorial problems: valid parenthesizations, non-crossing partitions, and polygon triangulations.
โ (a) n! is number of permutations, not BSTs. (b)(d) Wrong formulas.
Chapter Summary
5 Things Every Engineer Must Remember
- Trees model hierarchy โ file systems, org charts, DOM, category trees. If your data has parent-child relationships, trees are the natural structure.
- In-order traversal of BST = sorted output โ this single property powers database range queries, Kth element lookups, and ordered iteration. It's the BST's superpower.
- BST degenerates to O(n) with sorted input โ that's why production systems use self-balancing trees (AVL, Red-Black, B-Trees). Understanding the degradation teaches you why balanced trees exist.
- LCA has real applications โ Git's merge-base, Nykaa's product similarity, network routing's common ancestor. The recursive "split detection" algorithm is elegant and interview-critical.
- BST deletion with two children is the hardest case โ replace with in-order successor. Get this right in code and you've mastered 80% of tree manipulation. Every database index does this.
Master Comparison Table
| Operation | BST (Balanced) | BST (Skewed) | Array (Sorted) | Linked List |
|---|---|---|---|---|
| Search | O(log n) | O(n) | O(log n) | O(n) |
| Insert | O(log n) | O(n) | O(n) | O(1)* |
| Delete | O(log n) | O(n) | O(n) | O(1)* |
| Min/Max | O(log n) | O(n) | O(1) | O(n) |
| Sorted traversal | O(n) | O(n) | O(n) | O(n log n)โ |
*At known position. โ Must sort first.
Coming Up Next: Unit 5 โ Heaps, Hashing & Graphs
Trees organize data hierarchically. But what if you need the maximum element instantly? Every time Ola gets a ride request, it must find the nearest driver from 500,000 active drivers โ in under 5ms. A BST search is O(log n), but a heap gives you the min/max in O(1). And what about instant lookups? When PhonePe processes a UPI payment, it must verify the transaction ID against 100 million daily transactions โ in O(1). That's hashing.
In Unit 5, we'll master heaps (HeapSort, priority queues), hash tables (separate chaining, linear probing, double hashing), and graphs (BFS, DFS, Dijkstra's shortest path, Floyd-Warshall). We'll build a campus navigation system using Dijkstra's algorithm and a trending hashtag tracker using heaps. This is the final unit โ and the most powerful.