# Solutions to Final Exam

### Directions:

• Answer the problems on the exam pages.
• There are seven problems for 120 total points. Actual scale is A=100, C=68.
• If you need extra space use the back of a page.
• No books, notes, calculators, or collaboration.

```  Q1: 20 points
Q2: 15 points
Q3: 15 points
Q4: 15 points
Q5: 15 points
Q6: 20 points
Q7: 20 points
Total: 120 points
```

Question text is in black, solutions in blue.

Here is a code base from previous assignments, that is assumed to be available throughout the exam:

``````public class Dog {
private String name;
private int age;
// public get and set methods, two-parameter constructor
}

public class SledDog extends Dog {
private String breed = "Husky";
// public get and set methods, three-parameter constructor
}

public class LinearNode<T> {
private T element;
private LinearNode<T> next;
// public get and set methods, zero-parameter and one-parameter constructors
}

public class DogTeam {
private int size;
// public get and set methods, zero-parameter constructor
public SledDog removeLead ( ) {…}
public void switchLastTwo ( ) {…}
public SledDog removeYoungest ( ) {…}
public int countHuskies ( ) {…}
}

Code run before other code fragments:

Dog ace = new Dog ("Ace", 6);
Dog biscuit = new Dog ("Biscuit", 1);
Dog cardie = new Dog ("Cardie", 3);
Dog duncan = new Dog ("Duncan", 1);
SledDog balto = new SledDog ("Balto", 92, "Husky");
SledDog king = new SledDog ("King", 73, "Husky");
SledDog buck = newSledDog ("Buck", 108, "Mixed");

Some useful API information for L&C's code base:
public void push (T element);
public T pop ( );
public t peek ( );
public boolean isEmpty( );
public int size ( );
public String toString( );
public void enqueue (T element);
public T dequeue ( );
public T first ( );
public boolean isEmpty( );
public int size ( );
public String toString ( );
public interface ListADT<T> extends Iterable<T> {
public T removeFirst( );
public T removeLast( );
public T remove (T element);
public T first( );
public T last( );
public boolean contains (T target);
public boolean isEmpty( );
public int size( );
public Iterator<T> iterator( );
public String toString( );}
public void addAfter (T element, T target);}
public class SortingandSearching <T> {
public static <T extends Comparable<? super T >> boolean
linearSearch (T [ ] data, int min, int max, T target) {...}
public static <T extends Comparable<? super T >> boolean
binarySearch (T [ ] data, int min, int max, T target) {...}
public static <T extends Comparable<? super T >> void
selectionSort (T [ ] data) {...}
public static <T extends Comparable<? super T >> void
quickSort (T [ ] data, int min, int max) {...}
public static <T extends Comparable<? super T >> void
mergeSort (T [ ] data, int min, int max) {...}}
public T getRoot ( );
public boolean isEmpty( );
public int size( );
public boolean contains (T target);
public T find (T target);
public String toString( );
public Iterator<T> iteratorPreOrder( );
public Iterator<T> iteratorPostOrder( );
public Iterator<T> iteratorInOrder( );
public Iterator<T> iteratorLevelOrder( );}
public interface SetADT<T> extends Iterable<T> {
public T removeRandom ( );
public T remove (T element);
public boolean contains (T target);
public boolean isEmpty ( );
public int size ( );
public Iterator<T> iterator( );
public String toString ( );}

``````

• Question 1: Java Concepts Briefly explain the difference between the two concepts in each pair (2 points each).

• a) pop (in a stack) and peek (in a stack)

The pop operation removes and returns the most recently added element in the stack. The peek operation returns that element without removing it.

• b) postfix expression and prefix expression

A postfix expression has its operators after their arguments, e.g. "22+". A prefix expression has the operator first, e.g., "+22".

• c) ordinary array and circular array

A circular array stores its data in an interval of entries of an ordinary array, with variables to denote the beginning and end of the array. The interval may wrap around from the last element to the first, and both the beginning and end of the interval may move as elements are added and deleted from the structure being implemented (such as a queue or a deque).

• d) queue and deque

A queue is a linear data structure where the only element that may be removed is the one that has been there longest -- thus we add to one end of the queue and remove from the other. A deque is a linear data structure where we may add or remove elements from either end.

• e) binary search and linear search

Linear search of a data structure checks each element in turn to see whether it is the target element -- in the worst case it takes O(n) time. Binary search requires an ordered data structure -- it checks the middle element, compares it to the target, and recursively searches the first or second half of the remaining interval depending on the result. It takes O(log n) rounds and thus O(log n) time if each round can be done in O(1) time.

• f) `hasNext( )` in an iterator and ```next( )``` in an iterator

The `hasNext` method returns a boolean telling whether there is any element of the structure yet to be returned. The `next` method returns the next element of the structure.

• g) direct recursion and indirect recursion

Direct recursion is when a method calls itself. Indirect recursion is when a method calls another method which then calls the first method, possibly through other calls as well.

• h) `HashSet` and `HashMap`

Both are hash table implementations of (real) Java interfaces. A `HashSet` supports adding, removing, and testing membership of keys in a set. A `HashMap` stores pairs, each consisting of a key and a value. It can add and remove pairs, test membership for keys, and alos operate on the collection of values.

• i) chaining (in a hash table) and open addressing (in a hash table)

Both are methods of dealing with collisions. In chaining, multiple keys with the that map to the same entry in the table are placed in a linked list, either an ordinary one with pointers or an implicit one using an overflow area of the array. In open addressing, a second or subsequent key mapped to a given entry in the table is placed in a different entry computed in some way that can be reproduced when searching for the key.

• j) child (in a tree) and descendant (in a tree)

A child of a node is one of the nodes directly pointed to, below it. A descendant of a node is another node that can be reached by a path of zero or more "child" operations. (Technically, both the node itself and its children are counted among the descendants -- I did not take off points for missing this.)

• Question 2: Software Engineering -- Briefly discuss how you would make the following modifications to the code for the specified program, with specific reference to the code (5 points each):

• a) In Project #7 ("Some Games With Words"), the input string to the `list` method consisted of numbers (to be replaced by letters according to the phone code) and stars (to be replaced by any letter at all). Suppose we allow letters to appear in the input string as well, to be replaced only by themselves. (So the call `list ("J*zz*")` would return only `"jazzy"`.) What modifications to your Project #7 code would you need?

At some point during the `list` method, we try all the letters that match a particular digit or star in the input string -- three or four letters for a digit and 26 letters for a star. When we get a letter in the input string, we must try just that letter in our search.

• b) We looked at the game of Bingo in the last week of lectures, and saw how a `SetADT` object was useful for simulating a bag to draw the numbers. A `Bingo card` is a five by five array of numbers, where the first column's numbers are in the range 1-15, the second in 16-30, the third in 31-454, the fourth in 46-60, and the last in 61-75. No number can appear twice in a column, and the third square of the third column has no number. Explain (in English) how you would use one or more Set objects to create a random Bingo card.

We create five `Set<Integer>` objects, one for each column. We fill each with the 15 numbers in the appropriate range, then use the `removeRandom` five times for each column (four for the middle column) to get the numbers to put onto the card.

• c) In Project #6 ("Merge Sorting Dog Teams"), our input was an array of `DogTeam` objects. Indicate (in English) how you would write a method that would take such an input and output an array with the same `DogTeam` objects, but sorted in increasing order of average age. (For example, a team whose dogs averaged an age of 2.4 would come before one whose dogs averaged an age of 3.) (You may assume that each team in the array has at least one dog.)

We need to be able to compute the average age for each team, by adding the ages of the dogs in the team and then dividing by the number of dogs to get a `double` result. We could either add a `double averageAge` field to each `DogTeam` object, to be updated whenever a dog is added or removed, or just write a `compareTo` method for `DogTeam` that compares average ages. Once we can compare `DogTeam` objects by average age, we can use any comparison-based sorting method to sort the teams and place them in an array -- for example, we could put all the teams in a priority queue and then fill the array in order by removing the teams from the queue one by one. Note that we are now sorting teams, where Project #6 sorted dogs.

• Question 3: Tracing Code -- Determine the output value of each of the following blocks of code. In each case, assume that the class and variable definitions from above are in force when the code is run. Include a brief justification of your answer (5 points each).

• a)
``````
// This is an L&C priority queue, where "next" is the element
// in the queue whose priority is the smallest integer.  It breaks
// ties by earlier creation, i.e., it is FIFO on elements with the
// same priority.  Assume that the dogs from above have been created.
PriorityQueue<Dog> pq = new PriorityQueue<Dog> ( );
Dog z = pq.removeNext( );
z.setAge(1);
z = pq.removeNext( );
pq.removeNext( );
System.out.println (pq.removeNext( ).getName( ));
``````

We add Cardie and King, remove Cardie and set `z` to Cardie, add Duncan, change the age of `z` to 1, and put the new (Cardie, 1) object back in the priority queue. We now have three dogs in the queue. We next remove Duncan (who was added to the queue before Cardie), and then add Biscuit and Buck. We remove Cardie (who was added before Biscuit), and then remove Biscuit, so that the final string printed is "Biscuit".

• b)
``````
// Again, assume that the dogs from above have been created.
// This code is not in the DogTeam class.
DogTeam t = new DogTeam( );
t.switchLastTwo( );
s.setName ("Yukon King");
System.out.println (z.getName( ));
``````

We add King, Balto, and King to the team, and then switch the last two so that the first two dogs in the team are each copies of the same dog King. When we remove the first dog and change its name, the name of the second dog changes as well because it is the same dog (not just a different dog with the same attributes). So the string printed is "Yukon King".

• c) In a (not self-balancing) binary search tree of `Integer` objects, we successively add 3, 1, 4, 15, 9, 2, 6, and 5. Then we remove 4, 9, and 3 in that order. Draw the resulting tree.

We form the tree which has 3 at the root, left child 1 with right child 2, and right child 4 with a chain of 15 (right child), 9 (left child), 6 (left child), and 5 below it. When we remove the unary nodes 4 and 9, we replace them by their only children. This gives us root 3, left child 1 with right child, and right child 15 with left child 6 which has left child 5.

The method given by L&C to remove a node with two children replaces the node by its in-order successor, the smallest node in its right subtree. In this case we would replace the 3 with the 5, getting a five-node tree where the root 5 has left child 1 with right child 2 and right child 15 with left child 6. I gave full credit if you replaced the 3 with the 2, which is its in-order predecessor, but not for other stranger removal procedures.

• Question 4: Finding Errors -- Indicate whether the code fails to compile, throws an exception on reasonable input, or has clearly unintended output (5 points each):

• a)
``````
public int summation (int n) {
int count = 0, sum = 0;
while (count < n) {
sum += count;
if (count < n) sum += (count + 1);
if (count < n - 1) count += 2;}
return sum;}
``````

This code will compile and run, but never terminate if the input parameter n is odd. Once `count` becomes n - 1, the `while` loop will continue to execute over and over without changing the value of `count`. Note that it is not at all clear what "summation" the method is attempting to compute, so it is incorrect to say that it produces unintended output.

• b)
``````
// method to be added to DogTeam class
public SledDog[ ] toArray ( ) {
SledDog [ ] result = new SledDog[ ];
int index = 0;
while (cur != null) {
result [index] = cur.getElement( );
cur = cur.getNext( ); index++;}
return result;}
``````

This method will not compile because the array `result` is created without giving a size.

• c)
``````
// method to be added to DogTeam class
public void reverse ( ) {
DogTeam temp = new DogTeam( );
while (this.getSize( ) > 0)
temp.reverse( );
while (temp.getSize( ) > 0)
``````

The problem with this recursive method is that it has no base case -- it moves all but one element of the calling team to a temporary team and then recurses on that, so that the recursion will continue until the method is called from an empty team.

What happens then depends on the definition of `removeLead`. As defined in Discussion #4, `removeLead` runs and returns `null` when called from an empty team, so the call to `reverse` on an empty team will lead to another call to `reverse` on an empty team, and thus the recursion will continue until the method stack overflows. But if you instead said that `removeLead` from an empty team would throw an exception, I gave full credit (since I gave you neither the code nor the spec for `removeLead` on the handout).

Despite what many of you thought, this method would correctly reverse the team if it had a correct base case such as `if (size <= 1) return`.

• Question 5: Timing Analysis Indicate the big-O running time of each method or code fragment in terms of n. Include a brief justification of your answer. In each case, we use L&C's code base (5 points each):

• a)
``````
public static String binary (int n) {
if (n == 0) return "0";
if (n == 1) return "1";
if (n % 2 == 0) return binary (n/2) + "0";
return binary (n/2) + "1";}
``````

Each recursive call is with a parameter half the size of the calling method's parameter, and takes O(1) time excluding the call. There are thus log n calls before one of the O(1)-time base cases is reached, for a total time of O(log n).

• b)
``````
// n is size of input stack s
public static void reverse (StackADT<Dog> s) {
if (size <= 1) return;
StackADT<Dog> temp = new ArrayStack<Dog> ( );
while (s.size( ) > 1)
temp.push (s.pop( ));
reverse (temp);
Dog last = s.pop( );
while (!temp.isEmpty( ))
s.push (temp.pop( ));
s.push (last);}
``````

Each call is to a stack that is one element smaller than the calling stack, so the depth of the recursion is O(n) before the O(1)-time base case is reached. Each recursive call takes O(n) time to move the elements to and from the temporary stack, or rather O(k) where k is the size of the particular calling stack. The total time is thus proportional to n + (n-1) + (n-2) + ... + 2 + 1, which is O(n2).

• c)
``````
// uses L&C code base; n is size of input queue
public static void reorder (QueueADT<Dog> q) {
int n = q.size( );
if (n >= 11) return;
Dog d = q.first( );
QueueADT<Dog> temp = new ArrayQueue<Dog> ( );
for (int i = 0; i < 5; i++)
if (!q.isEmpty( )) temp.enqueue (q.dequeue( ));
while (!temp.isEmpty( ))
q.enqueue (temp.dequeue( ));
while (d != q.first( ))
q.enqueue (q.dequeue( ));}
``````

The running time is O(1) because if n is greater than 10, the method returns after doing only O(1) work. The maximum time it ever takes, then, is the maximum time it takes for the sizes up to 10, which is a constant, independent of n.

The first two loops take O(1) time as they move up to five elements each. The last loop moves n - 5 elements if n is large enough, so the time would be O(n) if not for the "cutout" statement for n greater than 10.

• Question 6: Short Code Writing Do both parts of the question (10 points each):

a) Recall the following code from Project #7 ("Some Games With Words"):

``````
public class PTNode {
private String elem;
private PTNode [ ] child = new PTNode [26];
public PTNode (String w) {elem = w;}
public String getElem ( ) {return elem;}
public void setElem (String w) {elem = w;}
public PTNode getChild (char ch) {return child[ch - 'a'];}
public void setChild (char ch, String w)
{child[ch - 'a'] = new PTNode (w);}
// next method added for this problem
public void setChildNull (char ch) {child[ch - 'a'] = null;}

public class PrefixTree {
private PTNode root;
private int size; // should be the number of leaves
// getters and setters, other methods
``````

Write a method ```public boolean removeString (String target)``` to be added to the `PrefixTree` class. If `target` is the element of a leaf of the prefix tree, your method should remove its node, remove any other leaves that become leaves as a result of its removal, and return `true`. (But don't ever remove the root node `""`.) If `target` is not a leaf or is not in the tree at all, the method should return `false` and do nothing else. You may assume that if the tree has more than one node, then all the leaves are at depth 5 as in the tree built in Project #7. Also, you may call the `contains` method for `PrefixTree`, which was provided to you for Project #7.

``````
public boolean removeString (String target) {
if (!contains (target) return false;
if (target.length( ) != 5) return false;
PTNode cur = root;
boolean done = false;
int depth = 0;
while (!done) {
int childCount = 0;
for (char ch = 'a'; ch <= 'z'; ch++)
if (cur.getChild (ch) != null) childCount++;
if (childCount == 1) {
cur.setChildNull (target.charAt (depth));
done = true;}
else {cur = cur.getChild (target.charAt (depth));
depth++;}}
return true;}
``````

b) Implement a stripped-down version of the generic class `HashSet<T>` as follows. Assume that the class `T` has a satisfactory `hashcode( )` method. A `HashSet<T>` object should have an instance field `int capacity` and an array of `ArrayList<T>` objects (as in L&C) of size `capacity`. Write a constructor ```public HashSet (int c)``` that will create an empty `HashSet` object with capacity `c`. Write methods ```public boolean add (Object o)``` and `public boolean contains (Object o)` to add an element and test membership for an element, respectively. The method `add` should return `false` if the object `o` is already there, but not throw an exception. (The parameter types of these two methods are `Object` to match `java.util.HashSet`, but your methods may throw exceptions (which you need not catch) if their inputs are not `T` objects.)

``````
public class HashSet<T> {
int capacity;
ArrayList<T> [ ] table;
public HashSet (int c) {
capacity = c;
table = (ArrayList<T> [ ]) new Object [capacity];
for (int i = 0; i < capacity; i++)
table[i] = new ArrayList<T> ( );}
public boolean add (Object o) {
if (contains(o)) return false;
T t = (T) o; // cast will fail if parameter is not a T
int h = t.hashcode( ) % capacity;
return true;}
public boolean contains (Object o) {
T t = (T) o; // case will fail if parameter is not a T
int h = t.hashcode( ) % capacity;
if (table[h].isEmpty( )) return false;
return table[h].contains(t);}}
``````

• Question 7: Long Code Writing (20 points): Sudoku is a game where a single player assigns numbers to the cells of a 9 by 9 game board. Each number must be an integer in the range from 1 through 9 (though of course the indices of the board are numbered from 0 through 8) and the board is filled successfully if each row, each column, and each box contains each number exactly once. (A box is a 3 by 3 sub board, whose set of rows and whose set of columns are each one of the sets {0, 1, 2}, {3, 4, 5}, or {6, 7, 8}. There are nine boxes, and each cell is in exactly one of them.)

Your task is to write an entire class `Sudoku`, where a `Sudoku` object will allow a user to play the game. You should store the board as a two-dimensional array of `int` values, using 0 to represent a cell that has no number yet. Your constructor should create a board with all 0 values. You should write three methods:

• `public boolean move (int i, int j, int k)`, which sets the value of cell (i, j) to be k,
• `public boolean check( )`, which returns whether the board is filled successfully, and
• `public void undo( )`, which reverses the effect of the last move that has not already been reversed by an `undo` command. If there are no moves that have not already been reversed, the `undo` command should do nothing and not throw an exception.

For regular credit, write the class so that a ```move (i, j, k)``` command is only value if the cell `(i, j)` is not yet filled, and `k` is in the correct range. The `move` method returns `true` if the move is valid.

For up to five points extra credit, allow moves to replace an existing number rather than just fill in a blank cell. This makes the undo method more complicated.

You may use any of L&C's given data structures such as `ArrayStack`, `ArrayQueue`, `ArrayList`, `ArrayBinaryTree`, or `ArraySet`, assuming that they correctly implement the corresponding ADT given on the handout.

``````
public class Move {
public int i, j, k, prev;
public Move (int newI, int newJ, int newK, int newPrev) {
i = newI; j = newJ; k = newK; prev = newPrev;}}

public class Sudoku {
int [ ] [ ] board = new int [9] [9];
Stack <Move> s = new Stack<Move>;

public Sudoku( ) {
for (int i = 0; i < 9; i++)
for (int j = 0; i < 9; j++)
board [i] [j] = 0;} // actually default makes this unneeded

public boolean move (int i, int j, int k) {
if (i < 0 || i > 8 || j < 0 || j > 8 || k < 1 || k > 9)
return false;
// use next line only if replacement not allowed
// if (board [i] [j] != 0) return false;
s.push (new Move (i, j, k, board[i][j]));
board [i][j] = k;
return true;}

public void undo ( ) {
if (s.isEmpty( )) return;
Move m = s.pop( );
// for when replacement is forbidden
// board [m.i] [m.j] = 0;
board [m.i] [m.j] = m.prev;}

public boolean check ( ) {
Set<Integer> s = new ArraySet<Integer>( );
Set<Integer> t = new ArraySet<Integer>( );
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (s.size( ) != 9) return false;
if (t.size( ) != 9) return false;
s = new ArraySet<Integer>( );
t = new ArraySet<Integer>( );}
for (int k = 0; k < 9; k += 3)
for (int l = 0; l < 9; l += 3) {
for (int kk = 0; kk < 3; kk++)
for (int ll = 0; ll < 3; ll++)
s.add (new Integer (board [k+kk] [l+ll]));
if (s.size( ) != 9) return false;
s = new ArraySet<Integer>( );}
return true;}}
``````

There were a number of variations on this, many of them correct. You could check the correctness of a row, column, or box by keeping a Set and rejecting if you ever found a number that was already in the set. Some people had the `move` method check whether the move created a conflict in a row, column, or box -- in that case the `check` method only had to ask whether the board was completely filled with nonzero numbers. Many people had undo methods that would only undo one move, where you were supposed to be able to use repeated undos to go back in time as far as you like.