Practice: Binary Search Tree as an Ordered Map

The goal of this exercise is to understand the recursive implementation of binary search trees and the range of performance based on how balanced a BST is.

1. Set up

Get the starter code from ~tvandrun/Public/cs345/omapbst. Make a new project in Eclipse. Familiarize yourself with the interfaces and given implementation. Out of the box all of the methods described in Section 4.1 should work. Confirm this by running the test RBSTMTest. (This does not test the remove() method, even though remove() is given to you, because remove() depends on min(), which you need to write.)

2. Finding the minimum element

Write the method RealNode.min(). This is just a few lines. Test using MinTest (this also tests remove(), which should work fine if you've written min() correctly).

3. Removing the minimum element

Removing a key from a BST, in general, is difficult. You read about it for today. However, the specific problem of removing the minimum element is much easier because the minimum element does not have a left child and so can be replaced by its right child (even if its right child is null). Write the method RealNode.removeMin(), which is only slightly longer than min(). Remember that the method should return the node that replaces the node on which the method is called. Nodes that are not deleted are their own replacement. Test using RemoveMinTest

4. Calculating the number of keys

The operation numKeys() doesn't have much to do with being an ordered map, and there's likely no good reason to have it as a public operation of the OrderedMap interface. However, it's easy to implement recursively and it will come in handy as a helper method in the following exercises. We've made it a public method so it will be easier to test.

Write the method RealNode.numKeys(). This is a one-liner. I think you had to do something like this in DMFP. Test using NumKeysTest.

5. Calculating a key's rank

For a key that exists in an ordered map, its rank is its position (counting from 0) among the keys in their comparable order. We extend this definition for a key not in the map by saying that its rank is the rank it would have if it were put into the map. This two-fold definition can be simplified by saying a key's rank (whether it's in the map or not) is the number of keys in the map that come before it.

At first glace this seems like a more complicated problem, but it's not. Difficult, maybe. But it can be computed by a simple rule implemented in RealNode.rank() using compareTo(), numKeys(), and recursive calls to rank(). Remember that the rank() method in the RealNode class should return the rank of the given key in the subtree rooted at that node. Locally to that method call you do not need to worry about the key's overall rank---forget, for the moment, about the rest of the tree.

Test using RankTest.

6. Calculating total depth

Now we turn our attention to evaluating trees and their performance. The order in which keys are inserted into the map affects the shape of the tree (how balanced it is) and thus affects performance. But how much difference does it make, and how does the typical case compare with the best and worst cases? In part 7 we will address these questions by timing runs, that is, evaluating the trees dynamically. But we should also consider evaluating them statically by finding a way to measure how balanced or imbalanced a tree is.

The statistic we'll measure is total depth, the sum of the depths of all the nodes. Write the method RealNode.totalDepth()---a one-liner. The parameter d is to tell the node receiving the call what depth it has in the tree, since the node has no way of computing that. Test using test.TotalDepthTest.

7. Experiments

Finally, inspect test.BSTExperiments and run the experiment. What do you observe?

8. To turn in

Please turn in the files you modified (should be just impl/RecursiveBSTMap.java) to

/cslab/class/cs345/(your user id)/bstomap

Thomas VanDrunen
Last modified: Tue Apr 17 14:57:53 CDT 2018