Trees & Binary Search Trees
Hierarchical data modeling: recursive DFS traversals (preorder, inorder, postorder), queue-based level order BFS, BST search/insert invariants, and binary heaps.
Hierarchical Nodes & The BST Invariant
A Tree is a non-linear hierarchical data structure consisting of nodes connected by directed edges without cycles. A Binary Search Tree (BST) enforces the fundamental ordering property: for every node, all keys in its left subtree are strictly less, and all keys in its right subtree are strictly greater.
- BST Inorder Traversal Property:An In-Order DFS traversal (Left -> Node -> Right) of a valid BST ALWAYS produces a strictly monotonically increasing sorted array.
- Search & Insertion Complexity:Average O(log N) in balanced trees (AVL, Red-Black); degrades to O(N) in skewed/degenerate trees.
- Tree Traversals (DFS vs BFS):DFS uses call-stack/explicit stack (Preorder: N-L-R, Inorder: L-N-R, Postorder: L-R-N); BFS uses a FIFO queue for Level-Order traversal.
- Validating BST with Strict Upper/Lower Bounds: Never just check
node.left.val < node.val. You must propagate strict valid ranges down the tree:validate(node, min_val, max_val)where left child must satisfy(min_val, node.val)and right child satisfies(node.val, max_val). - Lowest Common Ancestor (LCA) in BST: If both
pandqare smaller thancurr.val, move left; if both are larger, move right; the first node wherepandqsplit across left/right (or matchcurr) is the LCA in O(H) time! - Level-Order BFS Traversal: Push
rootinto a Queue. While queue is non-empty, recordlevel_size = len(queue)and process exactlylevel_sizenodes in a inner loop to separate tree levels.
The deepest node in a tree that has both node p and node q as descendants (where a node is permitted to be a descendant of itself).
Tree Height vs Node Count Relation
Binary Tree Traversals Comparison
| Traversal Type | Node Visit Sequence | Data Structure | Typical Application |
|---|---|---|---|
| Pre-Order DFS | Node -> Left -> Right | Call Stack | Tree Serialization & Cloning |
| In-Order DFS | Left -> Node -> Right | Call Stack | Sorted BST Output |
| Post-Order DFS | Left -> Right -> Node | Call Stack | Bottom-Up Deletion & Subtree Height Calculation |
| Level-Order BFS | Level by Level | FIFO Queue | Shortest Path & Level Averages |
When solving subtree diameter, maximum path sum, or tree balance problems, compute the node's property during Post-Order return (bottom-up) rather than computing top-down to avoid quadratic O(N^2) repeated subtree recalculations.
1class TreeNode:2 def __init__(self, val=0, left=None, right=None):3 self.val = val4 self.left = left5 self.right = right67def is_valid_bst(root: TreeNode) -> bool:8 def validate(node, low=float('-inf'), high=float('inf')):9 if not node:10 return True11 if not (low < node.val < high):12 return False13 return (14 validate(node.left, low, node.val) and15 validate(node.right, node.val, high)16 )17 return validate(root)1819# Construct Valid BST: [2, 1, 3]20tree_valid = TreeNode(2, TreeNode(1), TreeNode(3))21# Construct Invalid BST: [5, 1, 4, null, null, 3, 6] (4 has child 3 which is < 5)22tree_invalid = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6)))2324print('Tree 1 is Valid BST?', is_valid_bst(tree_valid))25print('Tree 2 is Valid BST?', is_valid_bst(tree_invalid))
Tree 1 is Valid BST? True Tree 2 is Valid BST? False
1struct TreeNode {2 int val;3 TreeNode *left;4 TreeNode *right;5 TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}6};78TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {9 TreeNode* curr = root;10 while (curr) {11 if (p->val < curr->val && q->val < curr->val) {12 curr = curr->left;13 } else if (p->val > curr->val && q->val > curr->val) {14 curr = curr->right;15 } else {16 return curr;17 }18 }19 return nullptr;20}
[Returns LCA node in O(Height) time and O(1) space]
Quick Syntax & Reference Sheet
| Concept / Rule | Syntax / Usage |
|---|---|
| BST Invariant | validate(node.left, low, node.val) and validate(node.right, node.val, high) |
| BFS Level Order | queue = deque([root]); while queue: for _ in range(len(queue)): node = queue.popleft() |
| LCA in BST | if p.val < root.val and q.val < root.val: return lca(root.left, p, q) |
Common Pitfalls & Developer Mistakes
Only checking direct children in BST validation (node.left.val < node.val)
Why it happens: A node in the right subtree could be smaller than the root ancestor, violating global BST invariant.
# Correct:
validate(node.left, min_bound, node.val)
validate(node.right, node.val, max_bound)