Exam Solution
Today was basically a discussion of the recent exam. The solutions with some discussion follow
Question #1
As provided, the add(...) method failed if the underlying array was fully populated. It signified this failure by returning false.Your task was to incorporate a call to the grow() method in order to allieveate this condition and allow the method to return only true.
This should have been easy -- replace the "return false;" with "grow();". Easy money!
public boolean add(Comparable item) { if (count >= items.length) grow();
Question #2
Question #2 asks you to write a grow() method very similar to those that we create in class. The key difference between the form on the exam and those that we build in class is that, in class, the array doubled each time. Here it grows by exactly two. Again, easy money -- "2 * items.length" becomes "2 + items.length".
public void grow() { Comparable[] biggerItems = new Comparable[items.length+GROW_AMOUNT]; for (int index=0; index < count; index++) { biggerItems[index] = items[index]; } items = biggerItems; }
Question #3
This question was really the bulk of the exam. But, with a little bit of thought, it falls easily. What are we asked to do? Return a subset of the container containing adjacent items within a particular range.So, what we need to do is find the starting point and the ending point, and add everything in between to the new collection. But, there are three complications:
- We don't know the starting point, we have to search for it
- The starting point might not be there in which case the test driver shows us that we should return an empty container
- The ending point might go past the end of the array. The test driver shows us that we should crop it.
So, let's sketch our solution and then go from there:
public OrderedContainer getNFrom (Comparable start, int n) { // Create the new container // Find the starting place, bail if none // Find the ending place, crop if beyond end // Copy the items into the new container // Return the new container }Now, let's fill int the code to handle each of these small steps:
public OrderedContainer getNFrom (Comparable start, int n) { OrderedContainer newContainer= new OrderedContainer(n); // Find the starting place, bail if none int startingIndex = getIndex(start); if (startingIndex == -1) return newContainer; // Found nothing // Find the ending place, crop if beyond end int endingIndex = start + n; if (endingIndex >= items.length) endingIndex = (items.length-1); // Copy the items for (int index=startingIndex; index <=; endingIndex; index++) { newContainer.add(items[index]); } // Return the new container return newContainer; }But, there's one last detail: The getIndex() method. I factored this out into a separate method for two reasons:
- It is complicated enough that it makes the code more readable to pull it out
- It might be useful as a helper method for other methods -- it seems like a common enought thing.
In the first iteration, I used a brute-force search:
private int getIndex(Comparable item) { int startingIndex = 0; for ( ; startingIndex < count; startingIndex++) { if (items[startingIndex].compareTo(start) == 0) break; } if (startingIndex == count) return -1; return startingIndex; }But we can, of course, do better than this -- the list is ordered. The search is best iplemented as a binary search, as below:
private int getIndex(Comparable item) { int left = 0; int right = count-1; int pivot = left + (right-left)/2; while (right >= left) { int difference = items[pivot].compareTo(item); if (difference == 0) return pivot; if (difference > 0) left = pivot-1; else right = pivot+1; pivot = left + (right-left)/2; } return -1; }
The Whole Thing
class OrderedContainer { private Comparable[] items; private int count; private static final int DEFAULT_SIZE = 2; private static final int GROW_AMOUNT = 2; private static final String NL = System.getProperty ("line.separator"); public OrderedContainer () { count = 0; items = new Comparable[DEFAULT_SIZE]; } public OrderedContainer (int size) { count = 0; items = new Comparable[size]; } public String toString() { String retString = "Count: " + count + ", Length: " + items.length + NL; for (int index=0; index < count; index++) { retString += items[index] + NL; } return retString; } public int size() { return count; } /* * PROBLEM #1 * MODIFY THIS METHOD *...so that it always returns true by growing */ public boolean add(Comparable item) { if (count >= items.length) grow(); int hole = count; for ( ; hole > 0; hole--) { if (items[hole-1].compareTo(item) > 0) items[hole] = items[hole-1]; else break; } items[hole] = item; count++; return true; } /* * PROBLEM #3 * Complete this method so that it returns the _n_ items * beginning with the one provided (_start_), or nothing * if start is not there. * * HINT: You may want a getIndex helper method... */ private int getIndex(Comparable item) { int left = 0; int right = count-1; int pivot = left + (right-left)/2; while (right >= left) { int difference = items[pivot].compareTo(item); if (difference == 0) return pivot; if (difference > 0) left = pivot-1; else right = pivot+1; pivot = left + (right-left)/2; } return -1; /* int startingIndex = 0; for ( ; startingIndex < count; startingIndex++) { if (items[startingIndex].compareTo(start) == 0) break; } if (startingIndex == count) return -1; return startingIndex; */ } public OrderedContainer getNFrom (Comparable start, int n) { OrderedContainer newContainer= new OrderedContainer(n); // Find the starting place, bail if none int startingIndex = getIndex(start); if (startingIndex == -1) return newContainer; // Found nothing // Find the ending place, crop if beyond end int endingIndex = start + n; if (endingIndex >= items.length) endingIndex = (items.length-1); // Copy the items for (int index=startingIndex; index <=; endingIndex; index++) { newContainer.add(items[index]); } return newContainer; // Found items } /* * PROBLEM #2 * MODIFY THIS METHOD... * so that the container is grown by EXACTLY TWO elements */ public void grow() { Comparable[] biggerItems = new Comparable[items.length+GROW_AMOUNT]; for (int index=0; index < count; index++) { biggerItems[index] = items[index]; } items = biggerItems; } public static void main (String[] args) { OrderedContainer oc = new OrderedContainer(); System.out.println ("****************************"); System.out.println ("***** THIS OUTPUT WILL NOT BE CONSISTENT UNTIL YOUR METHODS ARE CORRECT!"); System.out.println ("****************************"); System.out.println (""); System.out.println (""); System.out.println ("***** Testing grow by two...notice the growth by exactly 2."); System.out.println (""); System.out.println ("This container holds nothing (empty)."); System.out.println ("Should be Count: 0, Length: 2"); System.out.println (oc); System.out.println ("This container holds Apple, Banana, & Orange."); System.out.println ("Should be Count: 3, Length: 4"); oc.add("Orange"); oc.add("Apple"); oc.add("Banana"); System.out.println (oc); System.out.println ("This container holds Apple, Banana, Grapefruit, Orange, Pear, and Watermelon."); System.out.println ("Should be Count: 6, Length: 6"); oc.add("Watermelon"); oc.add("Pear"); oc.add("Grapefruit"); System.out.println (oc); System.out.println (""); System.out.println (""); System.out.println ("***** Testing getNFrom(...)..."); System.out.println (""); System.out.println ("1 from Apple is just Apple..."); System.out.println (oc.getNFrom("Apple", 1)); System.out.println (""); System.out.println ("2 from Apple is Apple, Banana..."); System.out.println (oc.getNFrom("Apple", 2)); System.out.println (""); System.out.println ("3 from Grapefruit is Grapefruit, Orange, Pear..."); System.out.println (oc.getNFrom("Grapefruit", 3)); System.out.println (""); System.out.println ("3 from Pear (2nd to last) is Pear, Watermellon..."); System.out.println (oc.getNFrom("Pear", 3)); System.out.println (""); System.out.println ("2 from NonExistant is nothing..."); System.out.println (oc.getNFrom("NonExistant", 2)); System.out.println (""); } }