The exam has 8 problems. Problems 1-5 are programming problems; problems 6-8 are written problems. The number of points for each problem appears in brackets next to the problem. There are 100 points total on the exam. All programming problems contribute to the same applet, and you can do them in any order, except where indicated. I suggest, however, that you tackle them in the given order.
The exam is open book in the sense that you can consult any books, handouts, or notes from this class to solve the problems. You may also follow the links from the course page and this exam 2 page to directly linked materials, e.g., the Java API and linked Wikipedia pages. However, you may not do any web searches, for instance to find answers on online discussion boards. You may not look at any code, except for the code examples from the course web page and the textbook. Also, you may not consult material or solutions from previous semesters or try to gain access to the source code for sample applets. I am the only person you may talk to about the exam. You may not consult or collaborate with other students or tutors. I will clarify the problems if you have trouble understanding them, but because this is an exam, I cannot help you solve the problems.
Please be very careful to protect your work from the access of others.
The deadline for both submissions is Monday 11/27 at 8:00am. (Those in section B can hand in the written answers at the beginning of class at 9:05). This is a hard deadline. You cannot turn in the exam later than this time, and you cannot use one of your 24 h extensions. Even if you are not finished with the exam by this time, you should turn in what you have, so that I can award partial credit. If a program does not work completely, it would be helpful if you include a note with your file that describes what aspects of your program do and don't work.
Several problems on this exam involve balanced trees, also known as AVL trees. Here are some basic definitions:
For the purpose of this exam, let the height of a tree be the number of nodes on the longest path from a leaf to the root of the tree. (This is different from Bailey's definition.) That is, an empty tree has height 0, a single node has height 1, and a full tree with 3 nodes has height 2.
Let the balance factor of a node be the difference in height between the node's right and left subtrees. For example, given a tree whose left subtree has height 4, and whose right subtree has height 2, the root's balance factor is 2-4 = -2. Note that if the balance factor of a node is negative, its left subtree is higher; if it is positive, its right subtree is higher; and if it is 0, the two subtrees have equal height. For a little more intuition about balance factors, let's consider full trees and complete trees. Full trees are "perfectly" balanced, in the sense that the balance factors of all nodes are 0. In a complete tree, most nodes will have a balance factor of 0, but there can be several nodes with a balance factor of -1 (meaning that the left subtree is one higher than the right subtree).
As we have discussed in class, it is desirable for trees to be "fairly balanced" (or "fairly full"). More precisely, we would like the height of a tree with N nodes to be O(log N). The reason is that the running time of most operations such as add, contains, and remove is proportional to the height of the tree. While it is possible to store values in a complete ordered tree, this is usually too restrictive. Instead, we will make our notion of "fairly full" more precise with the following definition:
We say a tree is balanced if the balance factor at every node in the tree is either -1, 0 or 1. In other words, the heights of the subtrees of a balanced tree differ by no more than one, and each subtree is in turn balanced as well. Conversely, a tree is unbalanced if it contains a node with a balance factor f such that |f| > 1 (the vertical bars mean "absolute value").
The applet below demonstrates the concept of balanced trees. Underneath each node it shows the node's balance factor, which is also encoded in the node's color. The "balance" button invokes a method to balance the tree. Problems 1-6 on this exam involve adding code to skeleton files to create an applet that functions like the one shown here.
All files required for this exam are contained in the file exam2.zip. Unzip this file in your cs201 directory. The resulting directory exam2 will contain the following files:
Assert.java - a copy of Bailey's file IntBSTApplet.java - the definition of the actual applet IntBST.java - binary search trees of integers * IntBSTops.java - operations on binary search trees IntBSTtest.html - an html file to run the appletThe only file you have to modify is marked with a star. However, you should study the other files as well, in particular IntBST.java, which is an adaption of Bailey's tree definitions to integers. The IntBST class is similar to the IntTree class, but each node contains a parent pointer well (which makes it possible to traverse the whole tree starting from any node). The file includes definitions of important methods that you will need to use to solve several of the programming problems (e.g., rotateRight() and rotateLeft()).
Import the exam2 directory into Eclipse as usual. The code provided compiles and runs. You can run it from within Eclipse by opening IntBSTApplet.java and selecting "Run As -> Java Applet", or from the command line using:
javac IntBSTApplet.java appletviewer IntBSTtest.htmlInitially, many parts of the applet are not yet functional. As you are completing the programming problems, more functionality will be added piece by piece, which allows you to test your code for each problem thoroughly before moving on to the next problem.
Once your methods work, the balance factor numbers underneath each node in the tree should be correct (the nodes will also change their color accordingly), and the text above the root of the tree should indicate whether the tree is balanced or not.
The current node only needs balancing if its balance factor is less than -1 or greater than 1. Let's assume it is less than -1 (the other case can be handled in a symmetrical fashion). This means that the left subtree is too high. To fix it, we perform a right rotation at the current node. As discussed in class, however, if the balance factor of the left child is greater than 0, we need to perform a left rotation of the left child before right-rotating the current node. If this is not done, the resulting tree will be unbalanced again. Here are illustrations of both cases (which you should use to test your method):
We compute the width of a tree by measuring the width of an (imaginary) picture of the tree which is drawn according to the following rules: All nodes are drawn as circles with radius 1. To draw a tree, we draw the circle for the root, and then each of the two subtrees below such that they touch the vertical line through the center of the root circle (from the left and the right, respectively). Some trees and their widths are shown below:
Note that some of the above rules are redundant. Try to define the each function in terms of the others as succinctly as possible.
public static String traverseLoop(IntBST t) { ... for (IntBST node = smallest(t); node != null; node = next(node)) ... ... }The implementation of next is a bit tricky. The next node in an in-order traversal could be above or below the current node in the tree, so we need to consider two cases, depending on whether the current node has a right child or not. If the current node has a right subtree, the next node is the left-most node in the right subtree (use your method smallest). Otherwise, the next node is above the current node, and can be found by repeatedly following the current node's parent pointers, while the nodes visited are right childs of their parents (use the method isRightChild). The desired next node is the parent of the first node encountered that is not a right child.
Let's look at examples for both cases. In the picture below, the node 2 has a right subtree, so the next node is the left-most node in the subtree (3). The node 5, however, has no right subtree, so the next node is found by following the parent pointers until the first node that is not a right child (2), and returning this node's parent (6).
Again, your code for the method should use while loops (rather than recursion) for full credit. Also, your method should not examine the values of the nodes. This is not necessary if the above strategy is followed. (In other words, your code should also work if the tree were not ordered, though we won't be testing that.) Once your method works, you should see the ordered sequence of values twice at the bottom of the applet.
public static void levelOrderWrite(IntTree t) { // prints out the contents of the tree t in level order Queue<IntTree> q = new QueueList<IntTree>(); q.add(t); while (!q.isEmpty()) { IntTree node = q.remove(); if (!isEmpty(node)) { System.out.print(" " + value(node)); q.add(left(node)); q.add(right(node)); } } System.out.println(); }What would happen if you replaced the queue with a stack? List the order in which the values of the example tree shown in problem 5 above would get printed (the tree containing the values 1-8 with 6 at the root). In general, would all nodes in the tree get printed? If yes, in what order? Justify your answer. (You are welcome to try it out, but you'll have to explain your findings - simply stating what happens is not enough.) Include your written answer with your paper submission.
Good Luck!