CS 201 - Exam 2 - 11/15/2017

DUE: Monday, 11/27, at 8:00am

This is the second of two midterm exams. Like the first one, it will count 15% towards your final grade; however, this exam is a take-home exam in place of the weekly homework. There are no afternoon labs and no evening tutoring sessions during the period of the exam.

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.

Turning in this Exam

You need to submit this exam both on paper and electronically:
  1. For the paper copy, print this Exam 2 cover page from a browser, staple your written answers to it, and turn everything in at the beginning of class on Monday 11/27. Don't forget to write and sign the honor code on the cover page.

  2. In addition, submit your code electronically using the Exam2 submission page.

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.


Balanced trees

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.

Your browser does not appear to support applets. Here is a static image:

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 applet
The 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.html
Initially, 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.


Problem 1 [15]: Computing the balance factors

Complete the methods height, balanceFact, and isBalanced in IntBSTops.java. Some of this code will look very similar to the IntTree code, but note that we don't provide static methods, so for instance you have to use t.left() instead of left(t), and t == null instead of isEmpty(t).

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.


Problem 2 [15]: The "balance" operation

Complete the method balance in IntBSTops.java. (You need to complete problem 1 first.) This method is called when the "balance" button is pressed. Note that in a "real" AVL tree, balancing would be performed automatically after each insertion or deletion (and only in the relevant subtree). In contrast, in our applet, the balance method should use the following strategy to balance the entire tree:

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):

  1. Left child (4)'s balance factor less or equal to 0: only right-rotate root (5):

        -->    

  2. Left child (2)'s balance factor greater than 0: left-rotate left child first (makes (4) new left child), then right-rotate root (5):

        -->    


Problem 3 [15]: Computing the width of trees

Complete the methods width, leftWidth, and rightWidth in IntBSTops.java. This methods are used for variable-width tree drawing, which you can select from the second pull-down menu.

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:

It can be seen that the following rules apply: The left width of a tree is the width of the part of the tree left of the vertical line through the center of the root. It can be seen that The right width is defined analogously.

Note that some of the above rules are redundant. Try to define the each function in terms of the others as succinctly as possible.


Problem 4 [10]: Finding the smallest and largest values

Complete the methods smallest and largest in IntBSTops.java. The smallest (i.e., left-most) node in a tree can be reached by following the left pointers from the root until a node without a left child is reached. Similarly, the largest (right-most) node is reached by repeatedly following right pointers. Your code for both methods should use a while loop (rather than recursion) for full credit. Once your methods work you can test them using the buttons "remove L" and "remove R", which should remove the first and last value in the ordered sequence.


Problem 5 [10]: In-order traversal using a loop

Complete the method next in IntBSTops.java. This method, together with the methods from problem 4, is used in the traverseLoop method, which perform an in-order traversal of the tree without the use of recursion:
    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.

Written problems


Problem 6 [12]: Complexity of tree and heap operations

What is the worst-case time complexity of each of the following operations? Give your answers in big-O notation and include brief justifications. In case a data structure doesn't normally support the operation, describe how it could be implemented and still give its complexity. For questions relating to heaps, assume min-heaps (as in the book and our examples). Include the answers with your paper submission.
  1. Finding the smallest number in a skew heap of N integers
  2. Adding an new number to a complete heap of N integers
  3. Finding the largest number in a complete heap of N integers
  4. Finding the smallest number in a complete binary search tree containing N integers
  5. Finding the largest number in an arbitrary binary search tree containing N integers
  6. Finding the largest number in a (full) 2-3 tree containing N integers


Problem 7 [13]: Tree drawing

Include the drawings and answers to the following questions with your paper submission.
  1. Draw all complete heaps containing the five values 1, 2, 3, 6, and 6. How many are there?

  2. Draw all complete binary search trees containing the six values 2, 4, 6, 8, 10, and 12. How many are there?

  3. Draw all full, ordered 2-3 trees containing the seven values 2, 3, 4, 5, 6, 7, and 8. How many are there?

  4. Draw all ordered AVL (i.e., balanced) trees containing the six values 2, 3, 4, 5, 6, and 7. How many are there?


Problem 8 [10]: Tree traversals

The solution to the extra-credit problem on homework 6 used a queue to print out the nodes of a tree in level-order:
    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!