Vocabulary
-
Root — no parent.
-
Leaf — no children.
-
Depth of a node: edges from root. Root depth = 0.
-
Height of a tree: max depth. Empty tree .
-
Level : all nodes at depth .
-
Full: every node has 0 or 2 children.
-
Complete: all levels full except possibly the last, which fills left-to-right.
-
Perfect: all levels full.
Perfect binary tree of height : nodes; leaves.
Node type
struct TreeNode {
int key;
TreeNode* left = nullptr;
TreeNode* right = nullptr;
// optional: TreeNode* parent;
}; BST invariant
For every node :
-
every key in the left subtree is ;
-
every key in the right subtree is .
(Duplicates disallowed by convention; if allowed, pick one side and document it.)
BST search
TreeNode* bstSearch(TreeNode* root, int k) {
while (root && root->key != k)
root = (k < root->key) ? root->left
: root->right;
return root;
} Cost: — balanced, degenerate.
BST insert
Walk as if searching; when you fall off a null pointer, that’s where the new node goes.
void bstInsert(TreeNode*& root, int k) {
if (!root) { root = new TreeNode{k}; return; }
if (k < root->key) bstInsert(root->left, k);
else if (k > root->key) bstInsert(root->right, k);
// else duplicate: policy decision
} BST remove: four cases
-
Leaf. Delete the node; parent’s pointer null.
-
Only left child. Parent’s pointer that child.
-
Only right child. Parent’s pointer that child.
-
Two children. Find in-order successor (leftmost of right subtree). Copy its key into the node to be deleted. Remove the successor (it has at most one child case 1 or 3).
Why successor? It’s guaranteed everything in left subtree and everything in right subtree — preserves the BST invariant.
Traversals
Order Visit pattern Use
Pre-order root, L, R copy/serialize In-order L, root, R sorted output Post-order L, R, root delete tree
All three are ; recursion depth .
In-order, iterative (useful!)
void inorder(TreeNode* root) {
std::stack<TreeNode*> s;
TreeNode* cur = root;
while (cur || !s.empty()) {
while (cur) {
s.push(cur); cur = cur->left;
}
cur = s.top(); s.pop();
visit(cur);
cur = cur->right;
}
} Set-ADT order ops on a BST (O(h) each)
find_min — walk left until null.
find_max — walk right until null.
find_next(x) (in-order successor):
-
if
x->rightnon-null: leftmost of right subtree; -
else: walk up via parent until you come from the left.
find_prev — symmetric. This is what hash tables can’t do — they cost for any of these.
Height vs. balance
Best: ops.
Worst: ascending/descending insert sequence “rope” tree, , ops become .
Random insertion order: expected (CLRS Thm 12.4) — a vanilla BST built from random data already behaves like a balanced one on average. Production uses red-black (ch. 9) anyway for the worst-case guarantee. Balance = the difference. Balance factor of a node = . AVL enforces .
BST vs. hash table vs. sorted array
BST hash sorted arr
find insert sorted iter range query yes no yes min/max
expected. BST numbers assume balance.
Parent pointers: why/why not
With parent: makes remove cleaner, supports iterator-style traversal, required for AVL / red-black rotations. Costs one extra pointer per node.
Without parent: smaller nodes; reach prev/next via recursion stack or reference-to-pointer idiom.
Top gotchas
-
Quoting BST ops as without saying balanced.
-
Removing with two children by replacing with the root of a subtree (invalid) instead of the in-order successor.
-
Forgetting that insertion order determines tree shape; sorted input linear chain.
-
Using
TreeNode*(value) instead ofTreeNode*&(reference) when a function needs to alter the parent’s pointer. -
Memory leaks from not deleting the tree (use post-order).