Skip to main content

Section 31.4 Binary Search Trees

Subsection 31.4.1 Definition and Properties

A binary search tree (BST) is a binary tree (max two children per node) that maintains a specific ordering property (the search tree property). For each node in the tree, all values in the left subtree are less than the node’s value, and all values in the right subtree are greater than or equal to the node’s value. This structure is designed to be the basis of an associative container, such as a map or set, where keys are stored in a sorted order and can be efficiently searched, inserted, and deleted. The structure of the tree will make those operations efficient, but it will not easily support random access by index.

Note 31.4.1.

Equal values can get different treatments. Some BST’s do not allow duplicates, others might handle them by storing multiple copies in a list at the node. However, if duplicates are allowed in either the right or left subtree, they must only be allowed on one side and be consistent throughout the tree.
The search tree property ensures that if we read off all the values in the tree in an in-order traversal, we get a sorted sequence. Try it out in this animation.

Activity 31.4.1. Binary Search Trees.

The binary search tree for the values \(15, 10, 20, 8, 12, 17, 25\) is shown below. We are prepared to do an in-order traversal of the tree. Use the > button to step through the traversal.
Instructions.
When the > button is highlighted, an animation is prepared to run. Use the Step button to step through the animation one step at a time.
When no animation is in progress, you can use the data controls to start a new animation.
You can click and drag on the animation area to pan around. Use the Ctrl + mouse wheel (or touchpad scroll gesture) while over the animation area to zoom in and out.
  • Core Controls.
    • << : Skip to the beginning of the animation.
    • < : Step backwards one step in the animation.
    • > : Step forward one step in the animation.
    • >> : Skip to the end of the animation.
    • Auto Step Speed Set to anything other than off to automatically step through the animation at the selected speed.
    • Zoom Set the zoom level. You can also use Ctrl + Mouse Wheel to zoom in and out.
  • Data Controls.
    • Value Use this area to enter a value when inserting, finding, deleting, etc...
The search property also lets us find a value without doing a full traversal of the tree. Starting at the root, we can compare the target value to the current node’s value. If they are equal, we have found the value. If the target is less than the current node’s value, we move to the left child and repeat the process. If the target is greater, we move to the right child and repeat. This process continues until we either find the value or reach a null pointer (indicating that the value is not in the tree).

Activity 31.4.2. Binary Search Find.

We are about to try to find(17). Use the > button to step through the find process.
When that is done, type 23 in the Value box and press the Find button to try to find \(23\) in the tree.
Instructions.
When the > button is highlighted, an animation is prepared to run. Use the Step button to step through the animation one step at a time.
When no animation is in progress, you can use the data controls to start a new animation.
You can click and drag on the animation area to pan around. Use the Ctrl + mouse wheel (or touchpad scroll gesture) while over the animation area to zoom in and out.
  • Core Controls.
    • << : Skip to the beginning of the animation.
    • < : Step backwards one step in the animation.
    • > : Step forward one step in the animation.
    • >> : Skip to the end of the animation.
    • Auto Step Speed Set to anything other than off to automatically step through the animation at the selected speed.
    • Zoom Set the zoom level. You can also use Ctrl + Mouse Wheel to zoom in and out.
  • Data Controls.
    • Value Use this area to enter a value when inserting, finding, deleting, etc...
Note how this process does not need to traverse the entire tree. We are able to take a single linear path from the root to the node we are looking for (or to a null pointer if the value is not present).

Subsection 31.4.2 Implementation

To implement a binary search tree, we will create a class that manages a tree structure of the Nodes we previously defined. It will store a pointer to the root node of the tree and provide functions for common operations like insertion, finding, and deletion. We will use the class IntBST as our working example. As the name implies, it will only store integers. But, we could easily turn it into a template class to store any type that supports a few comparison operators. (We will need to be able to compare values to decide if they are < or == to other values.)
class IntBST {
private:
    Node* root = nullptr;
public:
    bool find(int target);
    void insert(int value);
    void remove(int value);
};
Although most of our tree algorithms will be recursive, the public member function will not do that work. They will typically just be a wrapper that calls a recursive helper function starting at the root. For example, here is how we might implement the find member function:
Listing 31.4.1.
// Non-recursive member function that starts the recursive search
bool IntBST::find(int target) {
    // Start the recursive search at the root
    return findRecursive(root, target);
}
It simply calls a recursive helper function findRecursive, passing in the root of the tree and the target value to search for. When a value comes back from the helper, it is returned to the caller.
The recursive function that does the real work has two base cases where there is an easy, obvious answer. One is that we are at a node with the desired value. In that case, the answer to “do we have the value”. if clearly yes. The other is that we have reached a null pointer. In that case, we have run out of tree to search and the value is not present. So the answer is no.
In the general case, where we still have more tree to search, we need to make a recursive call. If the target value is less than the current node’s value, we make a recursive call on the left child. If the target value is greater than the current node’s value, we make a recursive call on the right child.
Listing 31.4.2.
bool findRecursive(Node* node, int target) {
    if (node == nullptr) {
        return false;  // Base case: target not found
    }
    if (node->data == target) {
        return true;   // Base case: target found
    }
    else if (target < node->data) {
        return findRecursive(node->left, target);  // Search left subtree
    }
    else {
        return findRecursive(node->right, target); // Search right subtree
    }
}

Note 31.4.2.

The find algorithm does have two recursive calls, but we will only ever take one of those paths. This will be important to remember when we analyze the performance of binary search trees.

Checkpoint 31.4.1.

Given this BST:
15 is the root. It has children 10 and 20. 10 has children 8 and 12. 8 has a left child 4. 12 has a right child 13. 20 has a left child 17 and a right child 25. 25 has a left child 32.🔗
Arrange the blocks in order to show the path taken by find(22). Any nodes that are not visited should not be included in your answer.

Checkpoint 31.4.2.

Given this BST:
15 is the root. It has children 10 and 20. 10 has children 8 and 12. 8 has a left child 4. 12 has a right child 13. 20 has a left child 17 and a right child 25. 25 has a left child 32.🔗
Arrange the blocks in order to show the path taken by find(11). Any nodes that are not visited should not be included in your answer.

Checkpoint 31.4.3.

We also can build an iterative version of find for a BST. Construct it using the blocks below.
You have attempted of activities on this page.