15. Binary Trees & Traversals
963 words
5 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# 15. Binary Trees & Traversals > **What problem does this solve?** Linked lists are linear — O(n) search.

15. Binary Trees & Traversals
What problem does this solve? Linked lists are linear — O(n) search. Trees are hierarchical, enabling O(log n) operations when balanced. Binary trees form the foundation for BSTs, heaps, AVL trees, and expression trees.
1. Tree Terminology
(Diagram)
| Term | Definition |
|---|---|
| Root | Topmost node (no parent) |
| Leaf | Node with no children |
| Parent | Direct ancestor of a child |
| Child | Direct descendant |
| Siblings | Nodes sharing the same parent |
| Depth | Distance from root (root = 0) |
| Height | Max depth of any node |
| Full tree | Every node has 0 or 2 children |
| Complete tree | All levels filled except possibly last, filled left-to-right |
| Perfect tree | All internal nodes have 2 children AND all leaves have same depth |
2. Binary Tree Node
python# runnable class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def __repr__(self): return f"TreeNode({self.val})" # Build 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)
3. Tree Traversals
Mental Model
- Preorder: Visit node, then left, then right (like reading a document: section, subsection)
- Inorder: Visit left, then node, then right (sorted order in BST)
- Postorder: Visit left, then right, then node (delete children before parent) (Diagram)
python# runnable class BinaryTree: def __init__(self, root=None): self.root = root def preorder(self, node=None): """Preorder: root → left → right""" if node is None: node = self.root result = [] def _traverse(n): if n: result.append(n.val) _traverse(n.left) _traverse(n.right) _traverse(node) return result def inorder(self, node=None): """Inorder: left → root → right""" if node is None: node = self.root result = [] def _traverse(n): if n: _traverse(n.left) result.append(n.val) _traverse(n.right) _traverse(node) return result def postorder(self, node=None): """Postorder: left → right → root""" if node is None: node = self.root result = [] def _traverse(n): if n: _traverse(n.left) _traverse(n.right) result.append(n.val) _traverse(node) return result def level_order(self): """Level-order (BFS) traversal using a queue.""" if not self.root: return [] from collections import deque result = [] q = deque([self.root]) while q: node = q.popleft() result.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) return result bt = BinaryTree(root) print(f"Preorder: {bt.preorder()}") # [1, 2, 4, 5, 3] print(f"Inorder: {bt.inorder()}") # [4, 2, 5, 1, 3] print(f"Postorder: {bt.postorder()}") # [4, 5, 2, 3, 1] print(f"Level: {bt.level_order()}") # [1, 2, 3, 4, 5]
Iterative Traversals
python# runnable def inorder_iterative(root): """Iterative inorder using explicit stack.""" result = [] stack = [] curr = root while curr or stack: while curr: stack.append(curr) curr = curr.left curr = stack.pop() result.append(curr.val) curr = curr.right return result def preorder_iterative(root): """Iterative preorder.""" if not root: return [] result = [] stack = [root] while stack: curr = stack.pop() result.append(curr.val) if curr.right: stack.append(curr.right) if curr.left: stack.append(curr.left) return result print(f"Inorder iterative: {inorder_iterative(root)}") print(f"Preorder iterative: {preorder_iterative(root)}")
4. Applications of Traversals
| Traversal | Use Case |
|---|---|
| Preorder | Copy/clone a tree, prefix notation |
| Inorder | BST sorted output, infix notation |
| Postorder | Delete tree, postfix notation, expression tree evaluation |
| Level order | BFS, shortest path, serialization |
5. Common Binary Tree Problems
Tree Height / Max Depth
python# runnable def max_depth(root): """Return maximum depth (height) of tree.""" if not root: return 0 return 1 + max(max_depth(root.left), max_depth(root.right)) print(f"Height: {max_depth(root)}") # 3
Check if Tree is Balanced (Height difference ≤ 1)
python# runnable def is_balanced(root): """Check if tree is height-balanced.""" def check(n): if not n: return 0 # Height = 0, balanced left = check(n.left) if left == -1: return -1 right = check(n.right) if right == -1: return -1 if abs(left - right) > 1: return -1 return 1 + max(left, right) return check(root) != -1 # Balanced tree balanced = BinaryTree(root) print(f"Balanced: {is_balanced(balanced.root)}") # True # Unbalanced tree unbalanced_root = TreeNode(1) unbalanced_root.left = TreeNode(2) unbalanced_root.left.left = TreeNode(3) print(f"Unbalanced: {is_balanced(unbalanced_root)}") # False
Serialize / Deserialize
python# runnable def serialize(root): """Convert tree to string using preorder with 'null' markers.""" def _serialize(n, parts): if not n: parts.append("null") return parts.append(str(n.val)) _serialize(n.left, parts) _serialize(n.right, parts) parts = [] _serialize(root, parts) return ",".join(parts) def deserialize(data): """Convert serialized string back to tree.""" parts = data.split(",") idx = [0] def _deserialize(): val = parts[idx[0]] idx[0] += 1 if val == "null": return None node = TreeNode(int(val)) node.left = _deserialize() node.right = _deserialize() return node return _deserialize() data = serialize(root) print(f"Serialized: {data}") restored = deserialize(data) print(f"Restored inorder: {inorder_iterative(restored)}") # [4, 2, 5, 1, 3]
Practice Questions
Q1. Draw the tree with preorder [1, 2, 4, 5, 3] and inorder [4, 2, 5, 1, 3].
Q2. What's the maximum number of nodes in a binary tree of height h?
Q3. Write a function to check if two binary trees are identical.
Q4. Find the diameter (longest path between any two nodes) of a binary tree.
Q5. What traversal would you use to delete all nodes in a tree?
Q6. How can you determine if a binary tree is a BST using inorder traversal?
AnswersA1. Root = 1. From inorder: left subtree = [4, 2, 5], right = [3]. From preorder: 2 is left child of 1. Recursively: 4, 5 are children of 2.A2. (2^{h+1} - 1) for a perfect tree of height h (root at height 0).A3. Check if both None → True. If one None → False. If values differ → False. Recurse on left and right children.A4. For each node, diameter = left_height + right_height. Max across all nodes is the answer.A5. Postorder — delete children before parent. Deleting root first would lose access to children.A6. Inorder traversal of a BST yields values in ascending order. If any pair violates this order, it's not a BST. Join Discord Previous14. Hash Tables — Dictionaries Under the HoodNext16. Binary Search Trees