This project is a C++ implementation of a Binary Search Tree (BST), focused on clarity, correctness, and testability. It was created as part of a service learning project to help teach lower-division students foundational data structures.
Included are:
- A fully templated, reusable
BST
class - Support for insertion, search, and deletion operations
- Recursive implementations of in-order, pre-order, and post-order traversal
- Comprehensive unit tests written using the Google Test (GTest) framework
The unit tests verify:
- Correctness of insert, search, and removal logic
- Handling of edge cases (e.g., removing root, leaf, one-child, or non-existent nodes)
- Tree behavior under sorted or unbalanced insertions
- Full clear and root replacement scenarios
A Binary Search Tree (BST) is a hierarchical data structure where each node follows these rules:
- Left child < Parent
- Right child > Parent
This ordering allows for efficient searching, inserting, and deleting of data.
Binary search trees allow for:
- Insert: O(log N) best case, O(N) worst case
- Search: O(log N) best case, O(N) worst case
- Delete: O(log N) best case, O(N) worst case
Note: Worst-case performance happens when the tree becomes unbalanced (e.g., like a linked list). A balanced tree ensures logarithmic performance.
Operations on BSTs follow binary search logic — halving the search space — which leads to logarithmic time complexity.
For example, searching in a balanced tree with 1 trillion nodes takes roughly 40 steps.
Recursion and trees naturally complement each other.
Recursive function calls form a recursion tree, mirroring how binary trees are traversed and processed.
We define a simple struct
for a tree node with:
- A value
- Left and right children (pointers)
FUNCTION INSERT(node, value):
IF node IS null:
RETURN new node with value
IF value < node.value:
node.left = INSERT(node.left, value)
ELSE IF value > node.value:
node.right = INSERT(node.right, value)
ELSE:
// value already exists — do nothing or handle duplicates
RETURN node
FUNCTION SEARCH(node, target):
IF node IS null:
RETURN null
IF target < node.value:
RETURN SEARCH(node.left, target)
ELSE IF target > node.value:
RETURN SEARCH(node.right, target)
ELSE:
RETURN node.value
We must handle 3 cases:
- Node has no children — delete it directly
- Node has one child — point the parent to the child
- Node has two children — find the in-order successor, replace, and remove
FUNCTION FIND_MINIMUM(node):
WHILE node.left IS NOT null:
node = node.left
RETURN node
- Locate the node
- If it has two children, find the minimum in the right subtree
- Replace the node’s value with the minimum
- Recursively delete that minimum node
Common ways to walk a binary tree:
- In-order (Left → Root → Right) — gives sorted values
- Pre-order (Root → Left → Right) — useful for tree copying
- Post-order (Left → Right → Root) — useful for deletion
- Level-order (Breadth-first traversal — not covered here)
FUNCTION IN_ORDER(node):
IF node IS null:
RETURN
IN_ORDER(node.left)
VISIT(node.value)
IN_ORDER(node.right)
FUNCTION PRE_ORDER(node):
IF node IS null:
RETURN
VISIT(node.value)
PRE_ORDER(node.left)
PRE_ORDER(node.right)
FUNCTION POST_ORDER(node):
IF node IS null:
RETURN
POST_ORDER(node.left)
POST_ORDER(node.right)
VISIT(node.value)