Practice problems for Test 2

The purpose of these practice problems is to simulate the experience of taking Test 2 by providing exemplary problems. On Test 2 you will be given an Eclipse window. You will also be able to view a local copy of the Java API. Internet connectivity will be turned off. Accordingly, you are strongly advised not to look at any resource besides the Java API when taking these practice problems.

(In fact, it's unlikely you'll need the Java API for either these practice problems or the test. I will provide implementations of all abstract data types that you would rely on---lists or queues or whatever. Also, Eclipse itself gives contextual help that provides most of the information you would get from the Java API. Access to the Java API is given only "just in case.")

Note that in the real Test 2 you will not be given a full set of JUnit tests.

Get the starter code from ~tvandrun/Public/cs345/test2-practice and set it up as you would for a project. The code is organized into one package per problem (not in the adt/impl etc packages as in the projects).

1. Bag implementation with logarithmic time operations.

Suppose you need an implementation of the Bag ADT for Strings that supports fast lookup and adding; "fast" here means logarithmic time. Further suppose that you know all of the potential keys ahead of time; that is, when the bag class is instantiated, before you have tallied any keys, you have a complete list or set of all the Strings that might be added to the set. (For example, you are counting word frequencies in a text, but you already know the full vocabulary in that text.)

Implement the class q1adt.FastBag, whose stubs are provided. Its base type is String; it is not generic. Specifically, implement it so that its operations add(), count(), and remove() operate in O(lg n) time, where n is the number of potential keys. Moreover, assume that the potential keys are passed to the constructor as an Iterator that returns the keys in sorted order.

(Note that the iterator received by the constructor is different from the iterator returned by the FastBag.iterator(); the order of the keys returned by the iterator you write is unspecified, and it should return the keys the number of times they occur.)

Test using q1adt.TestFastBag.

(Hint: The phrases "logarithmic time" and "keys in sorted order" should prompt you to think "binary search.")

2. Converting an AVL tree to a red-black tree

Consider the classes q2tree.AVLNode and q2tree.RBNode, which are reduced forms of the nodes for AVL trees and red-black trees. Note that, unlike in the project, null links are represented using the value null, not instances of a special "null node" subclass. Note also that, apart from being able to redden an RBNode, the nodes are immutable. Note further than an AVL node's height and balance are calculated in the constructor (once for all), but a red-black node's black height is computed on demand.

Write the method q2tree.AVLToRB.avl2rb() which takes an AVL tree represented by its root and produces an equivalent red-black tree, also represented by its root. The structure of the two trees should be identical. The method should be able to handle empty trees (ie, null references). The resulting red-black tree should have the red-black tree properties (consistent black height, no two reds in a row) except that the root may be red. Recursion is recommended.

Test using q2tree.TestAVLToRB.

3. Verifying the left-leaning red-black properties

Consider the class q3tree.RBNode, which like q2tree.RBNode is a reduced form of nodes for red-black trees, but unlike q2tree.RBNode does not contain code to compute the black height (instead it has a public non-final instance variable blackheight and cannot be reddened (the public instance variable isRed is final).

Write the method q3tree.VerifyLLRB.verifyLLRB() which takes a red-black tree represented by its root and does two things: (a) it returns whether or not that tree satisfies the left-leaning red-black tree property (consistent black height, no two reds in a row, all reds must be left children) except that the root is allowed to be red; and (b) it sets the instance variable blackHeight in all the nodes of the tree to the correct value, if the tree satisfies the properties. Note that the nodes do not have correctly computed black heights before the method is called; the method needs to do (b) for the subtrees in order to do (a) for the root. Recursion is recommended.

Test using q3tree.TestVerifyLLRB.

4. Determining whether a vertex is in a cycle

Recall that a cycle is a closed path in a graph, that is, a path that begins and ends at the same vertex. Consider also the interface q4graph.Graph which is exactly as it appears in the in-class examples and projects that accompanied the graph unit.

Write the method q4graph.CheckCycle.checkCycle() which takes a Graph (it will be an AdjListGraph, but that doesn't matter) and a starting vertex identified by an integer and determines whether there is a cycle of length at least one beginning and ending with that vertex. (Equivalently, this determines whether or not there is a cycle that includes this vertex, since it doesn't matter which vertex in the cycle is its beginning or ending point.)

(One could argue that all vertices are in a cycle, a trivial cycle of length zero. We don't count that. We're looking for cycles that have at least one edge. Note that, however, a vertex with a self loop has a cycle of length one.)

If it helps you think about it, you may assume that the graph is directed.

(Hint: There are two graph algorithms that each could be adapted to this problem.)

Test using q4graph.TestCheckCycle.

5. Computing the transpose of a graph

The transpose of a graph is a graph with the same (number of vertices) but with the edges reversed. That is, for graph G = (V, E), the transpose of G is GT = (V, ET) where edge (u, v) is in ET if and only if (v, u) is in E.

Consider the class q5graph.AdjListGraph which is exactly as it appeared in the in-class examples and projects from the graph unit. Note in particular the nested class ALGBuilder which is used to build a new AdjListGraph. Specifically, to build a new graph, use this pattern:

// make the builder
AdjListGraph.ALGBuilder builder = new AdjListGraph.ALGBuilder();

// ... add edges using builder.connect(u, v) ...

// now that we're done, get the graph
AdjListGraph g = builder.getGraph();

Write the method q5graph.ComputeTranspose.computeTranspose(), which takes a AdjListGraph and produces another AdjListGraph that is the transpose of the one given.

Test using q5graph.TestComputeTranspose.


Thomas VanDrunen
Last modified: Mon Mar 20 10:19:47 CDT 2017