Project: AVL Trees

1. Introduction to this and the following two projects

[The following talks about "three projects." Only the first two of the three are assigned projects to be turned in. The third (one left-leaning red-black trees) is recommended for practice, but does not need to be turned in.]

The goal of these next three projects (AVL trees, traditional red-black trees, and left-learning red-black trees) Is to understand how these three balanced binary search tree strategies work by implementing them. The three projects will use the same code base, and in some ways can be seen as a single, big project. Splitting them up into three projects, however, will help you spread out the work on them; for example, you are encouraged to start work on the AVL tree project (described here) after we learn AVL trees rather than waiting until we learn all the varieties of trees.

One part that is missing from this series of projects is an experimental section. I haven't developed that part enough yet. You are encouraged to write experiments to compare the running time for your own learning. (Caution: you'll probably find that you need fairly large amounts of data to see the effects.)

2. Set-up of this and the following two projects

As mentioned before, the code base is the same for all three projects, and this section will give an overview of the whole code. Copy the given code from ~tvandrun/Public/cs345/bal-tree and make an Eclipse project for it. As usual, you will find adt, impl, and test packages.

The most important part of the adt package is the Map interface, which has a slight modification from the previous versions we've seen: the remove() method signature has (ironically) been removed.

Because the various kinds of balanced BSTs have a fair amount of code in common, we have a complicated type heirarchy:

IterativeBSTMap does not share any code with the other types, since it takes a completely different approach. In fact, it's not even included with the given code of this project, though we have seen it in class.

The abstract class RecursiveBSTMap contains all the code for manipulating a binary search tree except for anything that would verify that the tree meets the properties of various balancing strategies and any code that would fix up a tree that is out of balance. That is deferred to the child classes. The child class BasicRecursiveBSTMap implements verification and fixup by... doing nothing.

RecursiveBSTMap also has a strategy object for verification. The verification strategies have their own class heirarchy (not shown), where there is a verification strategy class for each of AVL trees, traditional RB trees, and left-leaning RB trees. Don't modify classes like AVLVerify. You won't be turning them in. The idea is to keep you from (accidentally or dishonestly) changing the verification code so that it allows things that aren't really AVL trees or RB trees.

The classes AVLBSTMap, TraditionalRedBlackTreeMap, and LeftLeaningRedBlackTreeMap each provide the fix-up code---or they will, once you finish them, since that is your task in the three projects.

However, since the interesting code for these classes (including the fix-up code you need to write) are implemented recursively in the nodes, the type hierarchy for nodes is just as important and even more complicated. Here it is, but not showing the red-black tree node classes (to simplify it a bit):

This mainly mirrors the type hierarchy for the tree classes, but with another dimension: The trees are not going to have acutal null references, since that would require extra checks every time we use a link. Instead, "null" links will be references to special objects called null nodes. The advantage is that these objects can respond to the same methods as real nodes. Hence for every kind of tree, we have both a null node class and a "real" node class.

Take some time to understand how RecursiveBSTMap and its node classes are set up and how their code works. Notice how NullNode implements operations trivially.

An important method signature in Node is putFixup(). Most of your work in each of these three projects will be writing implementations for this in the "real node" classes, to rebalance the tree when it is in violation of the balance properties.

3. AVL trees

Turn your attention to AVLBSTMap. The interface AVLNode defines some additional operations for the nodes of AVL trees. AVL tree nodes will store information about the size, height, and balance of the subtree rooted at that node. Note that "balance" is defined as an integer which is the left height minus the right height. Thus if the subtrees have the same height, balance is zero. That doesn't mean the subtee is perfectly balanced, since the left and right subtrees might themselves be off balance. But it means that there is no problem with respect to each other.

These attributes could be computed on demand, recursively. However, that would require traversing the whole (sub-)tree, which would kill performance---it would defeat the purpose of using binary search trees. So instead we store that pre-computed information in the nodes. However, that information could become out of date when an insertion is made or when the tree rotates. We'll need to recompute those values. But even then, we don't want to traverse the whole tree; we recompute a node's height, size, and balance by assuming the node's children's values are correct and recomputing based on those values (for example, subtracting the children's heights to get a new balance value). This is done by recompute().

It is the responsibility of the code you write in putFixup() to balance and recompute as necessary. When the code is verified, the verification strategy will throw an ImbalanceException if the AVL property is violated. It will throw an IgnorantNodeException if a node has incorrectly stored height.

4. Your task

Write the body of AVLBSTMap.AVLRealNode.putFixup(). This is a hard task; my solution took around 60 lines of code, and there's no other way to do this than work through the details of the various cases. Here's a way to organize it:

Other hints:

Test using AVLBSTMTest.

5. Turn in

Copy the file you modified (AVLBSTMap) to your turn-in folder /cslab/class/cs345/(your id)/avl .

To keep up with the course, this should be finished by March 17.


Thomas VanDrunen
Last modified: Fri Feb 2 11:26:35 CST 2018