Lecture 19
What Is a Tree?
A tree is a collection of nodes connected by edges, with no cycles. Every node except the root has exactly one parent; every node can have zero or more children. Nodes with no children are called leaves.
Key terms:
- Depth of a node — number of edges from the root to that node
- Height of a tree — depth of the deepest leaf
- Subtree — a node together with all of its descendants
A binary tree restricts each node to at most two children, called left and right. Each node stores a value and two pointers.
You can build a binary tree by allocating nodes and wiring up the pointers manually:
Unlike a linked list, a binary tree branches in two directions. The shape of the tree — which values end up where — is entirely determined by how you wire the pointers. Later, when we add a rule for where to place each value, that rule becomes the BST invariant.
Traversals
To visit every node in a binary tree, you need an ordering. Four standard orderings exist:
| Traversal | Strategy | Visit order | Typical use |
|---|---|---|---|
| Inorder | Recursive | left → node → right | produces sorted output on a BST |
| Preorder | Recursive | node → left → right | copy or serialize a tree |
| Postorder | Recursive | left → right → node | delete or evaluate (leaves first) |
| Level order | Queue (BFS) | row by row, top to bottom | shortest path, level-wise processing |
The first three are recursive and follow a depth-first pattern — they go as deep as possible before backtracking. Level order is different: it visits every node at depth 0, then every node at depth 1, and so on. It uses a queue rather than recursion.
The queue enforces the row-by-row order: when you dequeue a node and process it, you immediately enqueue its children. Those children sit behind all other nodes at the same depth, so they are processed only after the current row is exhausted.
Running all four on the tree above:
The base case if (!node) return terminates the recursive traversals. Every leaf has
left == nullptr and right == nullptr, so both recursive calls hit this base case
immediately. Level order handles the same edge case by checking if (!root) return
before the queue is even created.
Binary Search Trees
The binary tree above has no rule governing where values go — you decide the shape manually. A binary search tree (BST) adds one invariant:
For every node, every value in its left subtree is strictly less than the node's value, and every value in its right subtree is strictly greater.
This invariant is maintained automatically by insert. Instead of wiring pointers by hand, you call insert and it walks the tree to find the correct position:
The payoff is O(h) search — where h is the tree height — instead of O(n). At each node, you can eliminate the entire left or right subtree from consideration based on a single comparison.
A second payoff: inorder traversal on a BST always produces values in ascending order. That property falls out of the invariant for free.
BST Implementation
Each public method delegates to a private recursive helper that receives the current node and returns the (possibly updated) node pointer. This lets insert and remove reattach subtrees without needing a parent pointer.
Insert and Find
insert walks the BST invariant to find the right slot, allocating a new node when it reaches nullptr. Duplicates fall through to return node with no allocation.
find exploits the same invariant to eliminate half the remaining tree at every step — O(h) instead of O(n).
Remove
Removing a node has three cases depending on how many children it has:
Case 1 — leaf: delete the node and return nullptr to the parent.
Case 2 — one child: delete the node and return its surviving child; it slides up one level.
Case 3 — two children: the node cannot simply be deleted — two subtrees would be left dangling. Instead, find the in-order successor: the leftmost node of the right subtree, which is the smallest value greater than the current node. Copy the successor's value into the current node, then recursively remove the successor from the right subtree (the successor has at most one child, so this reduces to case 1 or 2).
Always use the in-order successor (leftmost of right subtree) when removing a two-child node. Using an arbitrary child breaks the BST invariant for the remaining subtree.
Exercises
Q1.
Without running the code, predict what this program prints.
Q2.
Without running the code, draw the BST after all insertions, then predict the output.
Q3.
Each snippet has a bug related to trees or the BST. Identify what's wrong.
Q4.
Implement insert and find for the BST below.
Q5.
Implement remove for the BST below. Handle all three cases: leaf, one child, two children.
Practice Problems
- Maximum Depth of Binary Tree — Easy
- Binary Tree Inorder Traversal — Easy
- Validate Binary Search Tree — Medium
- Delete Node in a BST — Medium