Code for binary search trees may be found here.
We have already seen several examples of binary search trees. For example:
(8)
/ \
(2) (21)
/ \ /
(1) (5) (13)
/
(3)
Here the values stored as each node are themselves the keys (in this case
integers). One property of a binary search tree is that an in-order
traversal walks over the nodes in order of their keys (thus the name
in-order). Data maintained in a binary search tree is sorted by the key.
We can emulate a priority queue as long as the priorities are unique:
(homework, 2)
/ \
(cs367, 0) (Bagders, 3)
\
(clean room, 1)
Also notice how we can store more than just a key at each node.
Unlike a binary tree which is a data structure, a binary search tree is an ADT. That is, to use it, we don't need to know how it is represented.
The following are different binary trees:
(7) (5) / \ / \ (4) (9) vs. (4) (9) \ / (5) (7)but they represent the same binary search tree. Operations can we perform a BST include:
We can also test if the tree is empty, count how many values are stored in the tree and inquire as to the height of tree. Other functions may also be useful (like finding the smallest or largest elements in the tree) but they are not essential to what a BST is.
For example, we'd like to be able to build the above tree as something like this:
BST<String, int> bst; // bst is initially empty
bst.insert("homework", 2);
// (homework, 2)
bst.insert("cs367",0);
// (homework, 2)
// /
// (cs367, 0)
bst.insert("Badgers", 3);
// (homework, 2)
// / \
// (cs367, 0) (Bagders, 3)
bst.insert("clean room", 1);
// (homework, 2)
// / \
// (cs367, 0) (Bagders, 3)
// \
// (clean room, 1)
template <class Item, class Key>
class BST {
public:
...
bool search(const Key& k, Item& returnVal) const;
bool insert(const Item& v, const Key& k);
bool remove(const Key& v);
...
private:
BinaryTree<IKPair> *root;
...
};
So the BST class is really a wrapper around a binary tree.
bool search(const Key& k, Item& returnVal) const;The navigation can be done recursively roughly as:
Remove uses the same navigation through the tree as search, but then must adjust the tree to perform the deletion and to maintain the BST invariant.
There are three cases we need to consider for deletion:
(8) (8)
/ \ / \
(2) (21) (2) (21)
/ \ / ===> / \
(1) (5) (13) (1) (5)
/ /
(3) (3)
(8) (8)
/ \ / \
(2) (21) (2) (13)
/ \ / ===> / \
(1) (5) (13) (1) (5)
/ /
(3) (3)
(8) (8)
/ \ / \
(2) (21) (3) (13)
/ \ / ===> / \
(1) (5) (13) (1) (5)
/
(3)
or swap with the largest keyed-child in its left subtree, then remove:
(8) (8)
/ \ / \
(2) (21) (1) (13)
/ \ / ===> \
(1) (5) (13) (5)
/ /
(3) (3)
So, all the tree operations are proportional to height of the tree. But what is the height in relation to n --- the total number of nodes in the tree? It depends on the shape of the tree (which depends in what order nodes are inserted and deleted).
We might have a completely full binary tree:
(D)
/ \
(B) (F)
/ \ / \
(A) (C) (E) (G)
Or we might have a linear binary tree (for example, we insert into a
binary search tree in sorted order):
(A)
\
(B)
\
(C)
\
(D)
\
(E)
\
(F)
\
(G)
(or any of the many cases in between.)
A binary tree is balanced if each node has (roughly) the same number of descendants in its left subtree as it has in its right subtree.
Important fact: For balanced binary trees, the height is proportional to the base-two logarithm of the number of nodes in the tree: h = O(lg(n)).