[Instructions] [Search] [Current] [News] [Syllabus] [Glance] [Links] [Handouts] [Project] [Outlines] [Labs] [Assignments] [Quizzes] [Exams] [Examples] [EIJ] [JPDS] [Tutorial] [API]
Distributed: Monday, May 1, 2000
Due: 11 a.m., Monday, May 8, 2000
No extensions!
This page may be found online at
http://www.math.grin.edu/~rebelsky/Courses/CS152/2000S/Exams/exam.03.html.
Hash Tables
Lists
CursoredList.java
CursoredListPrinter.java
CursoredListSorter.java
CursoredListTester.java
ListCursor.java
NodeBasedCursoredList.java
NBCLTester.java
Queues
Trees
Utilities
There are seven (7) problems on the exam. You must answer five (5) problems. You will note that this exam has three (3) sections. You must answer at least one problem from each section. Each problem is worth twenty (20) points. If you answer more than five questions, I will only grade five of my choice. As in past exams, the point value of a question does not necessarily reflect its complexity or length.
Because I was unable to hand out this examination on Friday, April 28, I have changed the due date. However, I would prefer to have you return the exam on Saturday, May 6, 2000 so that we can go over the exam in Monday's class. Those turning in the exam by 5pm on May 6 will receive 5 points of extra credit. If you turn it in after May 5, you'll need to bring it to my house (1120 Main Street; 236-7445).
This examination is open book, open notes, open mind, open computer, open Web. You may use all reasonable resources. You may not use other people as resources. As always, you are expected to turn in your own work. If you find ideas in a book or on the Web, be sure to cite them appropriately.
This is a take-home examination. It is likely to take you about ten hours. You may use any time or times you deem appropriate to complete the exam, provided you return it to me by the due date. No late exams will be accepted. I will discuss the exam in class when I return it.
Because different students may be taking the exam at different times, you are not permitted to discuss the exam with anyone until after I have returned it. If you must say something about the exam, you are allowed to say ``This is definitely the hardest exam I have ever taken.'' You may also summarize these policies.
Answer all of your questions electronically. That is, you must write all of your answers on the computer and print them out. You should also email me a copy of your exam (a plain text file, please). Put your answers in the same order that the problems appear in.
I will give partial credit for partially correct answers. You ensure the best possible grade for yourself by highlighting your answer and including a clear set of work that you used to derive the answer. In any case, if you include a note about the amount of time spent on each problem or subproblem, I will give you five points of extra credit.
I may not be available at the time you take the exam. If you feel that a question is badly worded or impossible to answer, note the problem you have observed and attempt to reword the question in such a way that it is answerable. If it's a reasonable hour (before 10 p.m. and after 8 a.m.), feel free to try to call me in the office (269-4410) or at home (236-7445).
Some of the questions use or require Java code. If a question requires you to write working code, it will note that explicitly.
I will also reserve time at the start of classes next week to discuss any general questions you have on the exam.
William and Wilma Webhead have been searching on the Web for
information on linked lists. They've found an interesting
article that decries the use of a ``current'' element within
a list, On the importance of showing only good deigns [sic]
to students OR Lists with ``current'' considered harmful
by Joseph Bergin, on the Web at
http://csis.pace.edu/~bergin/papers/ListsWithCurrent.html.
For those not interested in reading the article and wondering
how you iterate lists, the Webheads note that the idea is that
you provide a separate ListCursor interface, so that a list
may have multiple cursors.
When the Webheads present this idea to Thelma and Theodore Theoretician, Thelma thinks ``Aha! That can make sorting much easier, as we may need more than one cursor.'' Theodore tells us that the typical selection sort algorithm relies on multiple cursors:
For each position in the list Swap the smallest remaining element into that position
Why does this need two cursors? One for stepping through the list (giving you the place to swap into) and at least one for finding the smallest element.
Hermione and Herman Hacker start to turn this idea for sorting into Java code.
import CursoredList;
import ListCursor;
/**
* Strange static stuff from question A on exam 3 of CSC152 2000S.
*
* @author Hermione Hacker
* @author Herman Hacker
* @author Herr Doktor Professor Rebelsky
* @version 1.0 of April 2000.
*/
public class CursoredListSorter
{
/**
* Sort a cursored list, comparing things as strings.
* Uses the selection sort algorithm.
*/
public static void sort(CursoredList list)
{
// Create a cursor. This is the destination of each swap.
ListCursor current = list.newCursor();
// A second cursor identifies the smallest known element
// of the list.
ListCursor smallest;
// Start at the beginning
current.reset();
// For each position in the list ...
while (current.inList()) {
// Find the smallest element in the sublist.
smallest = findSmallest(list,current);
// Swap the contents at the two positions.
list.swap(current,smallest);
// And move on.
current.advance();
} // while still in the list
} // sort(CursoredList)
/**
* Put a cursor on the location of the smallest element
* in a sublist that starts at a particular cursor. Uses
* string comparison to identify the proper place for an
* element.
* Pre: The sublist is nonempty.
* Post: Does not move the starting cursor.
*/
public static ListCursor findSmallest(CursoredList list,
ListCursor start)
{
// Our guess as to the smallest.
ListCursor guess = start.makeCopy();
// The string that corresponds to that guess.
String guessString =
list.valueAt(guess).toString().toLowerCase();
// A cursor we can use to iterate through the list. Start
// it at the next element.
ListCursor temp = start.makeCopy();
temp.advance();
// For each remaining position in the list
while (temp.inList()) {
// Get the contents.
String tempString =
list.valueAt(guess).toString().toLowerCase();
// If it's smaller, update our guess.
if (tempString.compareTo(guessString) < 0) {
guess.moveTo(guess);
guessString = tempString;
} // if it's smaller.
} // while
// That's it, we're done.
return guess;
} // findSmallest
} // class CursoredListSorted
To be helpful, they even provide two interfaces,
CursoredList and ListCursor
and some underlying implementations,
NodeBasedCursoredList and
NodeBasedListCursor.
Carla and Carl Caffeinated, not to be left out, note that there
are certainly other reasons that multiple cursors are helpful.
For example, when debugging, it may be useful to print out the
list. If the list does not contain a toString
method, we need to iterate the list to print it out. However,
if we were already using a cursor for something else, we've now
moved the cursor. (Some of you may have noticed the same problem
on a homework assignment.) Here's some code they've written.
import CursoredList;
import ListCursor;
import SimpleOutput;
/**
* A simple test of node-based cursored lists.
*
* @author Samuel A. Rebelsky
* @author Carla Caffeinated
* @author Carl Caffeinated
* @version 1.1 of May 2000
*/
public class CursoredListPrinter
{
public static void print(SimpleOutput out,
CursoredList list)
{
ListCursor printMe = list.newCursor();
printMe.reset();
while (printMe.inList()) {
out.print(list.valueAt(printMe) + " ");
printMe.advance();
} // while
out.println();
} // print(SimpleOutput, CursoredList)
} // class CursoredListPrinter
As you can tell from looking at the code, the Hackers never quite
finished writing the swap method in their implementation.
Finish that method for them. You should write working
code. Your implementation should be O(1).
Everyone has decided that testing would be much easier if they had a constructor that would turn arrays into lists. Here's their template.
/**
* Build a new cursored list containing the objects in
* the given array (in the same order as in that array).
*/
public NodeBasedCursoredList(Object[] initialValues) {
// STUB
} // NodeBasedCursoredList(Object[])
In fact, if they had that constructor, they could more easily test their printing routine (and, possibly, their sorting routine). For example,
/**
* A simple test program taken from section A of exam 3.
*
* @author Samuel A. Rebelsky
* @version 1.0 of April 2000
*/
public class Exam3A {
public static void main(String[] args) {
NodeBasedCursoredList stuff =
new NodeBasedCursoredList(args);
CursoredListSorter.sort(stuff);
CursoredListPrinter.print(stuff);
}
} // Exam3A
Finish this constructor. You should write working code.
The Theoreticians observe that there seems to be a significant logical
error in the inList operation for
NodeBasedListCursor. In particular, they suggest that we
consider the possibility that two cursors are on the same node in the
list, and that node is then deleted. The cursor used to delete the
element can be marked as ``no longer in the list'' but what about the
other one? Update inList so that it works properly.
Herman and Hermione Hacker have been working with Thelma and Theodore Theoretician on (you guessed it ...) Hash Tables. They've decided to use the ``scan the array for an open slot'' technique. Here's what they've written.
import Dictionary;
import Entry;
/**
* Hash tables that use the "shift into an empty space" strategy.
*
* @author Herman Hacker
* @author Hermione Hacker
* @author Thelma Thinker
* @author Thomas Thinker
* @author Samuel A. Rebelsky
* @version 1.1 of April 2000
*/
public class ShiftyHashTable
implements Dictionary
{
// +--------+----------------------------------------
// | Fields |
// +--------+
/**
* An array that contains all the elements in the
* hash table. An entry might be stored at location
* entry.getKey().hashCode() % entries.length
* (or after, if that cell is full). If null is stored
* in a particular cell, that cell is empty.
*/
protected Entry[] entries;
/**
* The number of entries in the hash table. Not
* the same as entries.length, since some (hopefully
* many) of the cells in entries will be null.
*/
protected int numEntries;
/**
* The last place the findKey operation looked. Useful
* for the add routine.
*/
protected int curLoc;
// +--------------+----------------------------------
// | Constructors |
// +--------------+
/**
* Build a new hash table that should work relatively
* well for up to maxEntries entries.
*/
public ShiftyHashTable(int maxEntries) {
this.numEntries = 0;
this.entries = new Entry[maxEntries*2];
} // ShiftyHashTable(int)
// +------------+------------------------------------
// | Extractors |
// +------------+
/**
* Determine if a particular key is in the dictionary.
*/
public boolean contains(Object key) {
// Find the key.
try {
findKey(key);
return true;
}
// If the key wasn't there, return false.
catch (Exception e) {
return false;
}
} // contains(Object)
/**
* Look up an entry in the dictionary.
*/
public Object lookup(Object key)
throws Exception
{
// Find the index for the key and return the
// corresponding entry. Since findKey throws
// an exception if the key is not found, we don't
// need to.
return entries[findKey(key)].getValue();
} // lookup(Object)
// +-----------+-------------------------------------
// | Modifiers |
// +-----------+
/**
* Add an entry to the dictionary.
*/
public void add(Object key, Object value)
throws Exception
{
try {
findKey(key);
// Hmm ... it's there. Can't re-add.
throw new Exception("Already there");
}
catch (Exception e) {
// It's not there. Use curLoc.
if (entries[curLoc] == null) {
++numEntries;
entries[curLoc] = new Entry(key,value);
}
else {
throw new Exception("No room");
}
} // catch (Exception)
} // add(Object, Object)
/**
* Delete an entry in the dictionary.
*/
public void delete(Object key)
throws Exception
{
throw new Exception("STUB");
} // delete(Object)
// +---------+---------------------------------------
// | Helpers |
// +---------+
/**
* Determine the index of a particular key.
* @exception Exception
* If the key is not in the table.
*/
protected int findKey(Object key)
throws Exception
{
// Determine the first place to look.
int startLoc = key.hashCode() % entries.length;
curLoc = startLoc;
// Step through until we (1) find the desired
// key; (2) find an empty space; or (3) return
// to where we started.
do {
// Is there no entry at the appropriate index?
if (entries[curLoc] == null) {
throw new Exception("Not found");
}
// Is an equal entry at the appropriate index?
if (key.equals(entries[curLoc].getKey())) {
return curLoc;
}
// Advance to the next index.
curLoc = (curLoc + 1) % entries.length;
} while (curLoc != startLoc);
// We've wrapped around or hit null. Give up.
throw new Exception("Not found");
} // findKey(Object)
} // class ShiftyHashTable
You've asked these folks why they haven't bothered to finish their
delete method. Theodore replies ``We've tried. However,
we couldn't figure out what to do when we add something with a hash code
of 1, then something with a hash code of 2, then something else with a
hash code of 1.'' Thelma explains ``While all that adding is easy,
we just don't know what to do when we delete. Our method worked for the
simple cases, but not for cases like that. Professor Rebelsky said that
our code was so bad that we were better off throwing it away. Please
help.''
Finish coding the delete method. You need
not write working code for this problem; a few syntactic mistakes are
fine, as long as you get the logic correct.
Theodore and Thelma, always thinking creatively, have decided that it's a bad design that their hash table can run out of room. So, they decide ``If the hash table gets more than 50% full, we should double its size.''. Here's what the have so far.
// If the hash table is too full
if (2*numEntries > entries.length) {
// Then expand the hash table
// STUB
}
Finish writing this and add it to your code. Once again, not write working code for this problem; a few syntactic mistakes are fine, as long as you get the logic correct.
Recall from class 47 we worked on an implementation of trees and algorithms for traversing those trees.
Your friends the Hackers, always thinking creatively, have decided to implement every one of the traversal strategies we discussed in class. Unfortunately, they're having difficulty with one ... breadth-first, postfix, left-to-right traveral. That is, they need an algorithm that prints the elements in each level from left to right and that prints the levels from last to first. Develop and implement that algorithm. You should write working code.
The Theoreticians, perhaps too theoretically, say ``Some algorithms require the depth of the tree. Shouldn't we have an O(1) way to get the depth?''
Augment both Tree.java and NodeBasedTree.java
to keep track of the depth of the tree. At minimum, you should support
a getDepth method.
Tuesday, 25 April 2000
Monday, 1 May 2000
Tuesday, 2 May 2000
Thursday, 4 May 2000
[Instructions] [Search] [Current] [News] [Syllabus] [Glance] [Links] [Handouts] [Project] [Outlines] [Labs] [Assignments] [Quizzes] [Exams] [Examples] [EIJ] [JPDS] [Tutorial] [API]
Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.
This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/2000S/Exams/exam.03.html
Source text last modified Thu May 4 10:25:16 2000.
This page generated on Thu May 4 10:28:01 2000 by Siteweaver. Validate this page's HTML.
Contact our webmaster at rebelsky@grinnell.edu