Q1: 10 points Q2: 15 points Q3: 15 points Q4: 15 points Q5: 15 points Q6: 10 points Q7: 20 points Total: 100 points
Format is similar to first midterm, some text explaining the question types is omitted.
Question text is in black, solution text 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 LinearDog<SledDog> leadNode;
private int size;
// public get and set methods, zero-parameter constructor
public void addToLead (SledDog newLead) {…}
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 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 interface OrderedListADT<T> extends ListADT<T> {
public void add (T element);}
public interface UnorderedListADT<T> {
public void addToFront (T element);
public void addToRear (T element);
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) {...}}
iterator
and Iterator
The method iterator
is the only method required by the
Iterable
interface. It should create a new object, an
iterator for the calling object, so that the iterator will return the
elements of the colling object one by one on request. That object
will
satisfy the Iterator
interface, which means that it has
methods next
, hasNext
, and
remove
.
(The remove
method is optional and often not implemented.)
remove( )
and remove(elem)
(both in
a java.util PriorityQueue
)
The zero-parameter remove( )
method removes and returns
the object in the priority queue that is "smallest" in the natural
order on that type (defined by a compareTo
method. The
one-parameter method remove(elem)
removes and returns
the object elem
if it is in the priority queue, and
throws
an ElementNotFoundException
if it is not there. The
latter
method is part of the List
interface.
java.util.AbstractList
and java.util.List
List
is an interface defining the operations that a list
must have. AbstractList
is an abstract class which has
code for some of the methods in List
. Since the
class is abstract, no objects of the class AbstractList
can be created. But we can extend AbstractList
to a
non-abstract
class by writing code for the other methods, and thus take advantage
of the code written in AbstractList
.
An ordered list has its elements sorted according to some defined
order relation, normally the natural order on the base type given by
its compareTo
method. An indexed list may be ordered or
not, but elements of an indexed list may be referenced by their
position in the list. Unlike an array, an indexed list renumbers its
elements
when an element is inserted into it or deleted from it.
The essential difference between these two concepts can be understood only by considering the difference between recursion and recursion.
We would first change the During the path search (either in the PQCell
class to add instance
fields
recording the state of the cell -- one way to do this would be to
add
boolean fields isVillage
and isCity
to the
existing
isTown
. We would also need to adapt the constructor
and toString
methods for Maze
so that
cells
could be represented in any of their five states (closed, just open,
village, town, city).
path
method or in
an auxiliary method called by both path
and
isPath
), a cell c comes off the queue and we get its open
unseen neighbors. For each of these neighbors, we then calculate
the number of spoons we could bring there if we went there directly
from c. This is where we calculate the tax, so we would redo this
code
to compute the tax as specified from the neighbor's status.
Our non-recursive method for Fibonacci numbers remembered the values F(n) and F(n-1) at any one time. It used these two values to compute F(n+1), then made this the new F(n) and made the old F(n) the new F(n-1). For Tribonacci numbers we would also remember F(n-2) at any one time, and make the old F(n-1) the new F(n-2) after computing F(n+1) from F(n), F(n-1), and F(n-2). We would also adjust the base case, so that n = 2 would become a base case and F(1) would be 0 instead of 1.
DogTeam
class, we have a method
removeYoungest
that removes and returns a
SledDog
that is no older than any other
SledDog
in the calling team. Describe (in English) how
I might use this method to sort a DogTeam
object's dogs
by
age, with the youngest dog at the lead.
If I repeatedly remove the youngest dog and add it to the lead of a
new DogTeam
, my new team ends up sorted by age, but with
the oldest dog at the lead. I can get the team I want by
creating
a new, third DogTeam
, then repeatedly removing the lead
dog of the second team and adding it to the lead of the third.
// using L&C's code base
String [ ] dogs = {"Cardie", "Ginger", "Ace", "Fred", "Duncan", "Ethel", "Biscuit"};
SortingandSearching.mergeSort (dogs, 0, 2);
SortingandSearching.quickSort (dogs, 2, 4);
SortingandSearching.mergeSort (dogs, 4, 6);
SortingandSearching.quickSort (dogs, 0, 2);
SortingandSearching.mergeSort (dogs, 2, 4);
SortingandSearching.quickSort (dogs, 4, 6);
for (int i = 0; i < 7; i++)
System.out.println (dogs[i]);
It doesn't matter whether we use merge sort or quick sort -- each of these calls puts an interval with three of the dogs in the correct relative order. If we represent the original order as CGAFDEB, the successive calls change the order to ACGFDEB, ACDFGEB, ACDFBEG, ACDFBEG, ACBDFEG, and ACBDEFG. So the output, on seven separate lines, is "Ace", "Cardie", "Biscuit", "Duncan", "Ethel", "Fred", and "Ginger". The code is doing something like Bubble Sort, sorting local groups of three rather than two, but it has not yet sorted the entire array when it stops -- another similar pass would do it.
public static String louis (int n) {
if (n == 0) return "x";
else if (n % 2 == 0) return "z" + louis (n - 1) + "z";
else return "y" + louis (n - 1) + "y";}
System.out.println (louis (4));
The original call to louis(4)
gives "z" +
louis(3)
+ "z". The second call gives "zy" +
louis(2)
+ "yz". The third gives "zyz" +
louis(1)
+ "zyz", the fourth gives "zyzy" +
louis(0)
+ "yzyz", and the final answer is "zyzyxyzyz".
public static boolean fannyLikes (String input) {
if (input.length( ) <= 1) return false;
char first = input.charAt(0); // first letter
String rest = input.substring(1); // all but first letter
if (first == rest.charAt(0)) return true;
return fannyLikes (rest);}
String [ ] test = {"dogs", "puppies", "cats", "kittens", "horses",
"colts", "fillies"};
for (int i = 0; i < 7; i++)
System.out.println (fannyLikes (test[i]);
This is based on the "Fanny Dooley" children's game -- the rule is
that Fanny Dooley likes only things with a double letter in their
name, so the answer (on seven separate lines) is "false", "true",
"false",
"true", "false", "false", "true".
The reason it works is that the
"return true" clause is reached if and only if for the current string
"input", the character "first" is equal to the first character of the
string "rest". If there is a double letter, this will happen for the
substring whose first and second letters are the same, and this value
will
be passed through the "fannyLikes (rest)" calls to be the result of
the original call. If there is no double letter, the "return true"
clause
will never be reached, and the recursive calls will eventually reach a
case
where the input is size 0 or 1, at which point "false" will be
returned, passed through the recursive calls, and returned as the output.
public class Pack implements Comparable<Pack> {
private Dog [ ] members;
public Pack (int size) {members = new Dog[size];}
// lots of other methods to add and remove dogs, etc.
public boolean compareTo (Pack other) {
if (this.members.length < other.members.length) return true;
else return false;}}
In order to implement Comparable<Pack>
, the class
needs to have a method compareTo (Pack other)
, but the
return value
of this method must be an int
, not a
boolean
. Without the int compareTo
method, the class will not compile because it does not have the
required method to implement the interface.
public static boolean isOK (String w) {
if (w.length( ) == 0) return false;
char first = w.charAt(0); // first letter
String rest = w.substring(1); // rest of string
if (first == rest.charAt(0)) return true;
else return isOK (rest + first);}
This is a recursive method that has a base case but will not necessarily
ever reach the base case, and thus continue making recursive calls
"forever" (or until the method stack overflows and causes a runtime
error).
If we call this method and the string w is not empty, the recursive
call will be on a string that is the same length as w (with the
first letter of w moved to the end). All the subsequent recursive
calls will be on strings of that length, never on a string of length
0 which would be the base case. We might return "true" if the
(corrected) if
statement
on the fifth line is reached -- this will happen if two consecutive letters
letters in w are the same. If w is a single letter, "rest" will be
the empty string and the test on the fifth line will cause a
StringIndexOutOfBoundsException
. Noting any of these
bad behaviors would get full credit.
// using L&C's code base, assume usual dogs are defined
UnorderedListADT<Dog> kennel = new Collection<Dog>( );
kennel.addToFront (ace);
kennel.addAfter (duncan, ace);
kennel.remove (ace);
kennel.removeFirst( );
try {System.out.println (kennel.first( ));}
catch (EmptyCollectionException e) { }
Since Collection
in java.util
is an
interface, not a class, it has no constructor. The attempt to call
its constructor on line 1 will not compile.
public static int twoToThe (int n) {
if (n <= 0) return 1;
return 2 * twoToThe (n - 1);}
This recursion does O(1) work to reduce the call with parameter n to a call with parameter n - 1. This will happen n times, with O(1) work each time, until the base case is reached and O(1) work is done to get an answer. The total work is thus O(n).
public static int bar (int n) {
int counter = 0;
for (int i = 0; i < n; i++)
counter += n;
if (counter == n * n) return counter;
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
counter--;
return counter;}
The first for loop executes n times doing O(1) work each time, so that
it takes O(n) time in all. The second for loop has another nested
for loop within it and O(1) work in the inner loop, so it would take
O(n2) time if it were ever reached. However, the
first for loop leaves the variable counter
equal to
n2, since n is added n times to the original 0. So the
test in line 5 is true, the return statement is reached, and the
code in lines 6-9 is never reached. The total time is O(n).
// method to be added to L&C's SortingandSearching class
// n is the combined size of the two arrays
public static <T extends Comparable<? super T>> boolean
sameElements (T [ ] one; T [ ] two) {
boolean soFar = true, foundYet = false;
for (int i = 0; i < one.length; i++) {
foundYet = false;
for (int j = 0; j < two.length; j++)
if (one[i].compareTo (two[j]) == 0) foundYet = true;
soFar = soFar && foundYet;}
for (int i = 0; i < two.length; i++) {
foundYet = false;
for (int j = 0; j < one.length; j++)
if (two[i].compareTo (one[j]) == 0) foundYet = true;
soFar = soFar && foundYet;}
return soFar;}
Each of the two arrays has length at most n. The nested for loop in lines
4-8 executes The second loop takes the same time as the first, so the total time
is O(n2) + O(n2) = O(n2).
one.length
times two.length
times, doing O(1) work in the inner loop, so its total time is
O(n2).
(The product of lengths is clearly no more than n2, and it
may be a constant times n2 because it is n2/4 if
the two lengths are equal.)
Following a definition that L&C give later in Chapter 11, here is a generic interface defining a priority queue:
public interface PriorityQueueADT<T> {
public void addElement (T object, int pri);
// places a new entry in the PQ with contents "object" and priority "pri"
public T removeNext ( );
// removes and returns the contents of the
// entry with lowest priority in the PQ
}
Write a generic class ListPQ<T>
that implements
PriorityQueueADT<T>
. Each
ListPQ<T>
object should have an
ArrayList
(from L&C's code base) of
PQEntry<T>
objects as an instance field. A
PQEntry<T>
object has two instance fields, its
contents of type T
and its priority of type
int
.
Define the class PQEntry<T>
as well and give it
a compareTo
method so that you can use the
ArrayList
to keep the nodes in the natural order, and use
the
ListADT
and OrderedListADT
methods to
implement the addElement
and removeNext
methods.
Both classes you write should have appropriate constructors.
public class PQEntry<T> implements Comparable<PQEntry<T>> {
private T contents;
private int priority;
public PQEntry (T con, int p) {contents = con; priority = p;}
public T getContents ( ) {return contents;}
public void setContents (T con) {contents = con;}
public int getPriority ( ) {return priority;}
public void setPriority (int p) {priority = p;}
public int compareTo (PQEntry<T> other) {
if (this.getPriority( ) < other.getPriority( )) return -1;
if (this.getPriority( ) == other.getPriority( )) return 0;
return 1;}}
public class ListPQ<T> implements PriorityQueue<T> {
private ArrayList<PQEntry> entries;
public ListPQ ( ) {entries = new ArrayList<PQEntry>( );}
public void addElement (T object, int pri) {
PQEntry<T> newEntry = new PQEntry<T>(object, pri);
entries.add (newEntry);}
public T removeNext ( ) {
return entries.removeFirst( );}}
In this question we will implement the "dishonest guessing game"
mentioned
in lecture. Write a class public class GuessingGame
that
will have one constructor with one int
parameter. When
we construct a GuessingGame
object with a positive
parameter n and run its play
method, it should first say "I am
thinking of a number in the range from 1 through (n). Can you guess
it in (k) tries?". Here "(n)" means to substitute the parameter, and
"(k)" means to substitute the largest number such that the player is
guaranteed to lose against dishonest play. (This number is the
largest
k such that 2k ≤ n.)
Assume that a method private int getGuess( )
has been
written for you. It returns an int
supplied by the
player -- don't worry about any exceptions it might throw. The game's
job is to (1) answer "too low" or "too high" to every guess, (2)
remember the largest number it has said to be too low, and the
smallest number it has said to be too high, and (3) when the player
has exhausted her guesses, supply the "number it was thinking of",
which must be consistent with all the answers it has given. Of
course, the way it does this is to choose its "too high" or "too low"
answer each time so as to leave it the largest possible interval in
which its number might be.
The game should say "Bad guess!" if the guess cannot be correct based on the answers it has already given. (But such a guess counts against the player's total.)
public class GuessingGame {
int initialTop, top, bottom;
public GuessingGame (int userTop) {
top = initialTop = userTop; bottom = 1;}
public int getGuess ( ) { } // assumed to be supplied
public play ( ) {
int limit = 0;
while (twoToThe(limit + 1) <= initialTop) limit++;
int guesses = 0;
System.out.println ("I am thinking of a number in the " +
"range from 1 through " + initial top + ". Can you guess" +
"it in " + limit + "tries?");
while (guesses < limit) {
int guess = getGuess( );
if (guess < bottom || guess > top) {
System.out.println ("What a stupid guess!");
guesses++;
break;}
int lowRange = (guess - 1) - bottom;
int highRange = top - (guess + 1);
if (lowRange >= highRange) {
System.out.println ("Too high! Guess again: ");
top = guess - 1;}
else {
System.out.println ("Too low! Guess again: ");
bottom = guess + 1;}
guesses++;}
System.println ("Sorry, you lose. My number was " +
(bottom + top)/2 + ".");}}
Last modified 12 November 2011