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.
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");
public class Cell {
private int x;
private int y;
private boolean isOpen;
// public get and set methods, two- and three-parameter constructors
}
public class SCell extends Cell {
private boolean seen;
// public get and set methods, constructors
}
public class QCell extends SCell {
private int distance;
private QCell parent;
// public get and set methods, constructors
}
In Project #2 Maze is a two-dimensional array of SCells, in Project #4 of QCells. Both versions have methods path and isPath.
Both are interfaces defining the basic operations of a queue, but
these operations have different names in many instances, and
Queue
has two different versions of some of the
operations.
For example, the enqueue
operation in
QueueADT
corresponds to add
and
offer
in Queue
-- add
throws
an excpetion if the enqueueing cannot be performed, while
offer
returns null
instead.
The this
object is the way to refer to the calling object
in instance methods. The this
constructor is the way
to call one constructor for a class from another constructor for the
same class -- calling this
with particular arguments
calls the constructor for the class (if any) whose signature matches
those
arguments.
A deque or double-ended queue is a variant of a queue that allows
adding or removing elements at either end. To dequeue an object is
to remove it from the queue -- the method to do this in L&C's
QueueADT
interface is in fact called dequeue
.
These two methods have exactly the same behavior -- they maintain a
stack
implemented as an array with the bottom at element 0 and the top
varying, doubling the array's size if asked to push when they are
full.
The class ProjectThreeStack
extends the class
DropoutStack
, but it never drops out any elements --
the characterisic behavior of a DropoutStack occurs in the
push
method, which is overridden in
ProjectThreeStack
.
These two classes both implement the StackADT
interface
and thus have methods with the same names and same signatures. The
difference is that ArrayStack is implemented with an array while
LinkedStack is implemented as a linked list of LinearNode
objects.
We make a new method chartDistance
in the
Maze
class that is similar to the path
method. The differences are (1) the new method's search never
"succeeds" because we remove the check for each node being the
destination, (2) once we empty the queue and end the search, we
assemble the printout by using two for loops to go over each cell of
the maze, printing the distance field if the cell is seen or a
placeholder if it is not, (3) clear the parent and distance fields
of every cell, and (4) return the printout.
Alter the main while loop for the search, so that it exits when the
stack is empty as well as when the boolean done
is set
to true. It should then return the value of done
,
which will be true if the destination was found and false if the
stack was emptied without finding it.
We could change L&C's CircularArrayQueue
into a dropout
queue
by changing just the enqueue
method so that it discards
its
argument and does nothing rather than calling the
expandCapacity
method. We could change
LinkedQueue
into a dropout queue by rewriting the
enqueue
method so that it checks the size against the
capacity before linking in the new element, and does so only if the
size is less than the capacity.
// using L&C's code base
QueueADT<Dog> q = new CircularArrayQueue<Dog>( );
q.enqueue(biscuit);
q.enqueue(king);
Dog dog1 = q.first( );
q.dequeue( );
q.dequeue( );
try{dog1 = q.peek( )}
catch (EmptyCollectionException e) {dog1 = king;}
System.out.println(dog1.getName( ));
Writing the queue from front to back, we have "B", then "BK", then we
stay
at "BK" while setting dog1
to biscuit
,
then we have "K", then an empty queue. The peek
method
throws
an EmptyCollectionException
which is caught, and in the
catch
code dog1
is reset to king
. We thus print
"King".
// new method in DogTeam class
public void merge (DogTeam other) {
if (isEmpty( )) leadNode = other.leadNode;
LinearNode ourLead = removeLead( );
while (!other.isEmpty( ))
addtoLead(other.removeLead( ));
addToLead (ourLead);}
// then run this code
DogTeam team1 = new DogTeam( );
team1.addToLead (balto);
team1.addToLead (king);
team1.addToLead (buck);
team1.switchLastTwo( );
team2.add(king);
team2.add(balto);
team2.merge(team1);
team1.removeLead( );
System.out.println(team2.removeLead( ).getName( ););
The merge method works as follows -- if Team 1 is created by adding Balto, King, and Buck in that order, so
that
Buck is at the lead. After the switch the order is (Buck, Balto,
King). Team 2 has King and Balto, with Balto in the lead. So the
merge gives us a Team 1 with contents (Buck, King, Balto, Balto,
King).
When we remove the lead dog and print the next we get "King".
a
and
b
are two DogTeam
objects and we call a.merge(b)
, all the dogs in
b
are added to a
. If a
is
empty, some strange things happen because I forgot to say
else after the if
, but fortunately in the
code afterward we only call merge
from nonempty
DogTeams.
In that case the elements of b are added to a in reverse order, just
after the lead dog of a. Note that team b is emptied in this case
as a side effect of the method.
// using the Maze class from Project #2
String [ ] init = {"101", "010", "101"};
Maze m = new Maze (3, 3, init);
try {
SCell sc = m.path(0, 0, 1, 1)[0];
System.out.println(sc);}
catch (ArrayOutOfBoundsException e) {System.out.println("Whoops!);}
catch (NullPointerException e) {System.out.println("Double Whoops!");}}
In this Maze, there is no path from (0, 0) to (1, 1). The
path
method returns an array of
SCell
objects, of length 0. When we try to set the
variable
sc
to the 0 element of this array, we then get an
ArrayIndexOutOfBoundsException
, which is caught, and we
print
"Whoops".
// new method in DogTeam
public SledDog last ( ) {
if (isEmpty( )) return null;
SledDog temp = leadNode.getElement( );
while (temp.getNext( ) != null)
temp = temp.getNext( );
return temp;}
This method will not compile because SledDogs do not have a getNext method -- the programmer has confused LinearNodes with their elements.
public class DogTeamDriver {
DogTeam rcmp = new DogTeam( );
for (int i = 0; i < 5; i++)
rcmp.addToLead(new SledDog ("Dudley", 4, "Husky"));
rcmp.addToFront(king);
System.out.println(rcmp.countHuskies( ));}
This class will not compile because it has code which is not in a method at all -- the programmer apparently forgot to declare the main method in which the code belongs. The compiler will accept the first line as declaring an instance field, but it will then probably say "identifier expected" when it sees the reserved word "for" when it is expecting either a field declaration or a method declaration. (This happened to a student doing Project #3 and it took quite a while for us to catch the error.)
// using L&C's code base
QueueADT q = new LinkedQueue<Dog>( );
q.enqueue(biscuit);
q.enqueue(duncan);
Dog temp = q.dequeue( );
q.enqueue(cardie);
while (!q.isEmpty( )) q.dequeue( );
try {temp = q.peek( );}
catch (EmptyCollectionException e) { }
System.out.println(q.size( ));
This also will not compile because the first line creates a
LinkedQueue<Dog>
object and tries to assign it
to a variable of a type that does not exist -- because
QueueADT
is a generic interface, it needs a type
parameter to be a usable type -- the programmer probably meant to
say QueueADT<Dog>
instead.
ArrayStack<Dog> s = new ArrayStack<Dog>( );
for (int i = 0; i < n; i++)
s.push(cardie);
Most of the n pushes take O(1) time each, but some of them cause resizing of the ArrayStack. Resizing an ArrayStack of size k takes O(k) time, so we might think that O(n) of these would take O(n2) time total. But as we've discussed, the sequence of resizings takes O(n) time total, because the time is proportional to 1 + 2 + 4 + 8 + ... + n = 2n. The pushes themselves take n times O(1) or O(n), so the total is O(n) + O(n) = O(n).
All we need to do is calculate the index n/2 and return that element of the array, which takes O(1) time. (Returning and removing that element would take O(n) time, because we would have to move the elements in the second half into new positions to fill the gap left by the middle element.)
// here "merge" is the method from 3b above
DogTeam team1 = new DogTeam( );
DogTeam team2 = new DogTeam( );
for (int i = 0; i < n; i++) {
team1.addToLead (buck);
team2.addToLead (king);}
for (int k = 0; k < n; k++)
team1.merge(team2);
Assembling the two teams of size n takes O(n) time. The first
merge operation also takes O(n) time. But as observed above, the
first merge operation leaves team2
with no elements.
Thus the second and subsequent merges take only O(1) time each, and
the total time for the code fragment is O(n).
A Maze is defined to be connected if every possible pair of open cells has at
least one path
from one to the other. Write an instance method public boolean isConnected( )
, to
be added to the Maze class, that
returns true if and only if the calling Maze is connected. You should use existing
methods from the Maze class, either from Project #2 or #4 (your choice).
This code needs to access individual cells of the Maze -- I will
assume here that we have a method public SCell getCell (int x,
int y)
that returns the cell at position (x, y). Without such
a method we would just reference the array field of the Maze directly,
but each of you may have given this field a different name.
public boolean isConnected ( ) {
boolean soFar = true;
for (int sx = 0; sx < width; sx++)
for (int sy = 0; sy < height; sy++)
for (int dx = 0; sx < width; dx++)
for (int dy = 0; dy < height; dy++)
if (getCell(sx, sy).isOpen( ) &
getCell(dx, dy).isOpen( ) & isPath(sx, sy, dx, dy))
soFar = false;
return soFar;}
The managers of the Iditarod sled dog race need to produce particular lists of dogs competing in the race, and would like to use your SledDog and DogTeam classes to do it. They have an array "entries" of DogTeam objects, containing all the dogs on all the teams entering the race. They want an array "ageTeams" containing the same dogs, but arranged into new teams where each team consists of dogs with the same age. You may assume that the ages of the dogs range from 0 through some constant MAX_AGE. The order of dogs within the new teams does not matter.
Write a method public DogTeam[ ] makeAgeTeams (DogTeam [ ]
entries)
to go into one of their classes -- note that it thus may use only public methods of the class DogTeam.
Assume that the DogTeams in "entries" are cloned before your method is run, so you need
not worry about destroying the copies of them you have. (Hint: Put all the dogs into
a queue, then take each dog in turn out of the queue and add it to the appropriate new
team.)
public DogTeam [ ] makeAgeTeams (DogTeam [ ] entries) {
DogTeam [ ] ageTeams = new DogTeam [MAX_AGE + 1];
for (int j = 0; j < MAX_AGE + 1; j++)
ageTeams[j] = new DogTeam( );
QueueADT<SledDog> q = new LinkedQueue<SledDog>( );
for (int i = 0; i < entries.length; i++)
while (!entries[i].isEmpty( ))
q.enqueue (entries[i].removeLead( ));
while (!q.isEmpty( )) {
SledDog thisDog = q.dequeue( );
ageTeams[thisDog.getAge( )].addToLead (thisDog);}
return ageTeams;}
Last modified 15 October 2011