Skip to Content
Course content

161: Implementing a Binary Search Tree in C++

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, so you've mastered linked lists, and now we're branching out—literally. A Binary Search Tree (BST) is where things get interesting because we're moving from linear storage to a hierarchical one. The magic of a BST is that it keeps data sorted as you insert it, which makes searching incredibly fast. I've seen a lot of people overcomplicate the implementation, but if you keep the recursive nature of the tree in mind, it actually clicks pretty quickly.

How do I structure the Node and the Tree class so it's actually manageable?

You don't want your main logic floating around with raw pointers everywhere. The cleanest way to do this in C++ is to define a small Node struct and wrap it inside a BST class. The Node is just a container; the BST class is where the actual "brain" of the operation lives.

struct Node {
    int studentId; 
    Node* left;
    Node* right;

    Node(int id) : studentId(id), left(nullptr), right(nullptr) {}
};

class BinarySearchTree {
private:
    Node* root;
    // We'll put our recursive helpers here
public:
    BinarySearchTree() : root(nullptr) {}
    void insert(int id) {
        root = insertRecursive(root, id);
    }
    // ... other methods
};

I use a studentId here because it's a concrete example of something you'd actually want to index. Notice how the Node is a simple struct. Since it's just a data carrier, it doesn't need a bunch of complex methods; the BinarySearchTree class handles the heavy lifting.

I get the concept, but how do I handle the recursion for inserting nodes without losing the root?

This is where most learners trip up. They try to handle the root as a special case in every single function. Instead, use a private recursive helper function that returns a Node*. This allows the tree to "re-wire" itself as the recursion unwinds.

Node* insertRecursive(Node* current, int id) {
    // Base case: we've found the empty spot where this ID belongs
    if (current == nullptr) {
        return new Node(id);
    }

    // If the ID is smaller, go left; if larger, go right
    if (id < current->studentId) {
        current->left = insertRecursive(current->left, id);
    } else if (id > current->studentId) {
        current->right = insertRecursive(current->right, id);
    }

    // Return the (potentially updated) current node back up the chain
    return current;
}

By returning the node, you're essentially saying, "Here is the updated version of this subtree." It's a very elegant pattern. If the root was nullptr, the first call returns the new node, and root = insertRecursive(root, id) captures it perfectly.

How do I actually find something in the tree without checking every single node?

This is the whole point of the BST. Because we've maintained the rule that "left is smaller, right is larger," we can discard half the remaining tree with every single comparison. It's exactly like looking up a word in a physical dictionary.

bool search(Node* current, int id) {
    if (current == nullptr) return false; // Not found
    if (current->studentId == id) return true; // Found it!

    if (id < current->studentId) {
        return search(current->left, id);
    } else {
        return search(current->right, id);
    }
}

I usually implement the public search method as a simple wrapper: bool contains(int id) { return search(root, id); }. This keeps the user of your class from having to deal with the Node* root pointer, which should stay private to avoid someone accidentally deleting the whole tree from main().

If I want to print the elements in sorted order, how do I do that?

There are a few ways to traverse a tree, but for a BST, the "In-Order Traversal" is the gold standard. If you visit the left child, then the current node, then the right child, you will magically print the IDs in perfectly ascending order. It's almost like the tree is doing the sorting for you.

void inOrderTraversal(Node* node) {
    if (node == nullptr) return;

    inOrderTraversal(node->left);       // Visit left subtree
    std::cout << node->studentId << " "; // Visit current node
    inOrderTraversal(node->right);      // Visit right subtree
}

Believe it or not, this is one of the most satisfying moments in learning data structures. You can insert 50, 20, 70, 10, 30 in any order, and inOrderTraversal will always spit out 10, 20, 30, 50, 70. Just remember that you'll need a destructor in your BinarySearchTree class to recursively delete all those nodes, or you'll have a memory leak on your hands.




📋 Practical Task

Implement a Student ID Registry with a Range Search

Your task is to extend the Binary Search Tree implementation. Create a class called StudentRegistry that uses a BST to store student IDs. In addition to the standard insert and search functionality, implement a method called printStudentsInRange(int min, int max).

This method should traverse the tree and print only the student IDs that fall between the min and max values (inclusive), in ascending order. To do this efficiently, your function should avoid visiting subtrees that couldn't possibly contain values within your range.

  • Requirement 1: Implement the insert and inOrder traversal.
  • Requirement 2: Implement printStudentsInRange using a recursive approach.
  • Requirement 3: In your main function, insert the following IDs: 105, 50, 150, 25, 75, 125, 175.
  • Requirement 4: Call printStudentsInRange(60, 130) and verify that it prints 75, 105, 125.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.