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.
Format is similar to the midterms, some text explaining the question types is omitted.
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 LinearNode<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 StackADT<T> {
public void push (T element);
public T pop ( );
public t peek ( );
public boolean isEmpty( );
public int size ( );
public String toString( );
public interface QueueADT<T> {
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 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) {...}}
public interface BinaryTreeADT<T> {
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 void add (T element);
public T removeRandom ( );
public T remove (T element);
public SetADT<T> union (SetADT<T> set);
public boolean contains (T target);
public boolean equals (SetADT<T> set);
public boolean isEmpty ( );
public int size ( );
public Iterator<T> iterator( );
public String toString ( );}
A piece of code runs in O(1) time if there is a constant c such that the code never takes more than c time on input of any size. It takes O(n) time if there is a c such that it never takes more than cn time on any input of size n (when n is sufficiently large).
public class Foo
and public class Foo
extends Object
There is no difference between the two as every class automatically
extends Object
.
An object is-a Stack if it extends the Stack class (or in L&C's terms,
implements the StackADT
interface). It has-a stack if
it
has an instance field of type Stack (or StackADT
).
Both are linear structures made of nodes. In a singly linked structure each node except the last has a pointer to the following node. In a doubly linked structure each node except the first also has a pointer to the node before it.
Both are recursive O(n log n) time sorting algorithms. Merge sort divides the set to be sorted in half arbitrarily, sorts each half recursively, and merges the two sorted halves. Quick sort picks a pivot element, divides the set into those elements less than the pivot and those greater than it, sorts each piece recursively, and places the sorted pieces in the correct place.
A recursion is grounded if any legitimate input will cause it to terminate, since every seqeunce of calls will eventually reach a base case. It is ungrounded if some legitimate input causes an infinite sequence of recursive calls, so that the process never terminates.
Both are linear structures, but an ordered list is kept so that the elements are in order according to some given comparison operation. When an element is added to an ordered list, there is normally only one place in the sequence it may go to preserve the order. An element added to an unordered list may legally go anywhere.
java.util.List
and java.util.LinkedList
List
in java.util
is an interface giving all
the methods for an indexed list. LinkedList
is a class
implementing that interface, built as a linked structure.
Both are tree structures. A heap is partially ordered in that each parent element is smaller than its children (in a min-heap), and a heap of a given size has a fixed structure, almost balanced with all leaves left-justified on the last level. A binary search tree has the property that for every node x, all elements in the node's left subtree are less than x and all nodes in its right subtree are greater than x. There is no requirement that it have any particular structure.
The inorder traversal traverses all nodes in the left subtree of x before it traverses x, and all elements in the right subtree after it traverses x. The level-order traversal begins with the root, then all nodes at depth 1, then all at depth 2, and so forth until it traverses the nodes of maximum depth.
I would create an int
array numberAtLevel
of
size 6, so that numberAtLevel (i)
will record the
number of nodes at level 1. I would initialize this array to all
0's,
then traverse the tree in one of our four standard ways,
incrementing the appropriate counter for each node. How does a node
know what depth it is at? In our implementation the string stored
in a node of level i was always at depth i.
If my chains are stored as ArrayList
objects, for
example, each chain knows how many elements it has. Whenever I add
an element, I check whether the add makes a chain that is too big,
and trigger a resize operation if it does.
directions
that would take a source and destination cell and return a string
describing a sequence of north, east, south, and west moves that
would
take you from the source to the destination, using only open cells?
(It should return the string "no path" if there is no path.)
The output of our path
method in each case was an array
of cells if the path existed, and an empty array if not. So my
directions
method just needs to call
path
. If the output has length 0, I report "no path".
Otherwise I go through the output, determine which length-1 move was
needed to go from each cell in the array to the next, and write a
description of that move to my output.
// uses L&C code base, Dog class, standard dogs from above
Set<Dog> kennel = new ArraySet<Dog>( );
kennel.add (cardie);
kennel.add (duncan);
boolean b = kennel.contains (duncan);
boolean c = kennel.contains (biscuit);
if (b || c) kennel.remove (cardie);
kennel.add (duncan);
Dog z = kennel.removeRandom ( );
System.out.println (z.getName( ));
Cardie goes in, and Duncan goes in on top of her. We set b to true
and c to false. Since b || c
is true, we remove
Cardie.
We add Duncan, which has no effect since he is already in. The
random
Dog removed must be Duncan since he is the only dog in the set.
So we print "Duncan".
Dog terrier = duncan;
Dog retriever = cardie;
terrier.setName (cardie.getName( ));
retriever.setName (duncan.getName( ));
if (terrier == cardie) duncan.setName ("Biscuit");
System.out.println(cardie.getName( ) + " " + duncan.getName( ));
There are only two dogs involved here, though each has multiple names.
We set the name of the dog originally named "Duncan" to "Cardie". The
second setName thus has no effect, as it sets Cardie's name to
"Cardie"
which it is already. But there are still two different dogs, and
terrier
is one and cardie
the other.
So the third setName operation does not happen and we print "Cardie Cardie".
removeMin
operations. What are the resulting
contents of the array? (Assume that the initial capacity was 10.)
empty
3
3 7
3 7 4
3 7 4 23
3 7 4 23 19
2 7 3 23 19 4
2 7 3 23 19 4 9
3 7 4 23 19 9
4 7 9 23 19
7 19 9 23
// method to be added to the DogTeam class
public SledDog [ ] toArray ( ) {
SledDog [ ] ret = new SledDog [size];
if (size == 0) return ret;
ret[0] = leadNode.getElement( );
int index = 0;
LinearNode<SledDog> cur = leadnode;
while (cur.getNext( ) != null) {
index++;
ret[index] = cur.getElement( );
cur = cur.getNext( );}
return ret;}
The method is correct for size 0 and 1, but if the while
loop actually operates we get a
clearly unintended output. The lead node's element is placed
into ret[1]
as well as ret[0]
, and
the subsequent elements are placed one entry further down than they
belong. The last element in the list is never placed into the array.
// method to be added to the DogTeam class
public DogTeam clone( ) {
DogTeam ret = new DogTeam( );
if (size == 0) return ret;
LinearNode<SledDog> cur = leadNode;
leadNode = cur.getNext( );
size--;
LinearNode<SledDog> newLead =
new LinearNode<SledDog>(cur);
ret = this.clone( );
ret.addToLead (newLead);
return ret;}
This won't compile because the variable cur
is a node,
but the one-parameter constructor for
LinearNode<SledDog>
expects a
SledDog
.
Otherwise the logic works.
// method to be added to the Maze class of projects 1 and 2
public int countOpenCells ( ) {
count = 0;
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
if (cells[i, j].isOpen( )) count++;
return count;}
This won't compile because the variable count
is never declared.
// method to be added to DogTeam class
public void rotate ( ) {
if (size <= 1) return;
LinearNode<SledDog> old = leadNode;
leadNode = leadNode.getNext( );
rotate ( );
LinearNode<SledDog> second = leadNode.getNext( );
leadNode.setNext (old);
old.setNext (second);}
This method rotates a team of size n by removing the lead element, rotating the remaining team, and replacing the old lead elmement in the second position. So rotating a team of size n means rotating a team of size n-1, which means rotating a team of size n-2, and so on until we reach a team of size 1, which is rotated by doing nothing. There are O(n) recursive calls, and each one involves only O(1) work, so the total running time is O(n).
// method to be added to DogTeam class
public void reverse ( ) {
if (size <= 1) return;
if (size == 2) {
SledDog first = removeLead( );
SledDog second = removeLead( );
addToLead (first);
addToLead (second);
return;}
SledDog oldLead = removeLead( );
reverse( );
SledDog oldRear = removeLead( );
addToLead (oldLead);
reverse( );
addToLead (oldRead);}
Here reversing a team of size n means reversing two teams of size n-1, which means reversing four teams of size n-2, eight teams of size n-3, and so on. We eventually reach the base case but we have O(2n) calls to it for O(2n) total time.
// uses L&C code base; n is size of input queue
public static void reorder (QueueADT<Dog> q) {
int n = q.size( );
if (n <= 6) return;
Dog d = q.first( );
QueueADT<Dog> temp = new LinkedQueue<Dog>( );
for (int i = 0; i < 3; i++)
temp.enqueue (q.dequeue( ));
for (int j = 0; j < 3; j++)
q.enqueue (q.dequeue( ));
while (!temp.isEmpty( ))
q.enqueue (temp.dequeue( ));
while (d != q.first( ))
q.enqueue (q.dequeue( ));}
On a queue of length greater than 6, we move the first three elements
onto temp
, move the next three elements to the back of
the
queue, and put the first three elements on the back. All this is
O(1),
since the enqueue and dequeue operations are each O(1). The final
while loop moves elements from front to back until d, the original
first
element, is again the first element. Since d is the third from last
element when this loop is executed, it takes O(n) time and the total
time is O(n).
a) Write a method public DogTeam merge (DogTeam
other)
to be added to the DogTeam
class. If x
and y are two DogTeam
objects that are sorted by
age, then x.merge (y)
should return a team
consisting
of exactly the dogs from both x and y, also sorted by age. ("Sorted
by age" means that each dog in the team has an age less than or equal
to the dog following it.) You may assume that no individual dog
appears in both teams.
If either team is not sorted by age, the method should throw an
UnsortedException
-- assume that this exception class
has been defined.
As we take elements off the lead of the two teams and put them onto a
new team, we assemble the team we want backwards, which we can
fix by reversing it after we are done.
public boolean isBad ( ) {
// is the lead dog of this team older than the second dog?
if (size <= 1) return false;
int leadAge = leadNode.getElement( ).getAge( );
return (leadAge > leadNode.getNext( ).getElement.getAge( ));}
public DogTeam merge (DogTeam other) {
DogTeam rev = new DogTeam( );
while (!this.isEmpty( ) && !other.isEmpty( )) {
if (this.isBad( ) || other.isBad( ))
throw new UnsortedException( );
if (this.getLeadNode( ).getElement( ).getAge( ) <
other.getLeadNode( ).getElement( ).getAge( ))
rev.addToLead (this.removeLead( ));
else rev.addToLead (other.removeLead( ));}
if (!this.isEmpty( )) {
if (other.isBad( )) throw new UnsortedException( );
while (!other.isEmpty( ))
rev.addToLead (other.removeLead( ));}
while (!this.isEmpty( )) {
if (this.isBad( )) throw new UnsortedException( );
rev.addToLead (this.removeLead( ));}
DogTeam ret = new DogTeam( );
while (!rev.isEmpty( ))
ret.addToLead (rev.removeLead( ));
return ret;}
b) Write a method public boolean hasDuplicate( )
to
be added to the DogTeam
class. It should return two if
any two of the dogs in the calling team are the same dog
according to the equals
method of the
SledDog
class. You may use any of the data structures
defined by L&C, but not an iterator for DogTeam
unless you write it. Your method should leave the calling
DogTeam
exactly as it was before the call.
public boolean hasDuplicate( ) {
if (getSize( ) <= 1) return false;
ArraySet<SledDog> as = new ArraySet<SledDog>( );
LinearNode<SledDog> cur = leadNode;
while (cur != null) {
as.add (cur.getElement( ));
cur = cur.getNext( );}
return (getSize( ) == as.size( ));}
Your job here is to write code to maintain the waiting list for
admission to a course. Students are stored as Student
objects, and the roster of those students already in the course is
stored as an object roster
of type
SetADT<Student>
. The class Student
has a compareTo
method so that the "smallest" student in
a collection is the one most eligible to fill a vacancy in a course.
You are to define a class Waitlist
extending the class
ArrayOrderedList<Student>
, with these three methods:
Waitlist ( )
that establishes an empty waiting list,
addStudent (Student s)
that
puts a new student into the list,
fillCourse (SetADT<Student>
roster, int capacity)
that takes students from the calling waiting list, in order of
their eligibility, and adds them to
roster
until either (a) the size of roster
reaches
capacity
or (b) the waiting list is empty. It is all
right to add a student to roster
who is already in the
course -- the SetADT
methods will ensure that there is
no duplicate entry and that the size is maintained correctly.
public class Waitlist extends ArrayOrderedList<Student> {
public Waitlist ( ) {super( );}
public void addStudent(Student s) {
add (s);}
public void fillCourse (SetADT<Student> roster, int capacity) {
while ((roster.size( ) < capacity) && !isEmpty( ))
roster.add (removeFirst( ));}}
Last modified 12 December 2011