CS 61B  Homework 8
                        Due 5pm Sunday, April 18, 2004

This homework will give you practice implementing sorting algorithms, and
illustrate how sorting linked lists can be different from sorting arrays.  This
is an individual assignment; you may not share code with other students.

Copy the Homework 8 directory by doing the following, starting from your home
directory.  Don't forget the "-r" switch in the cp command.

    mkdir hw8
    cd hw8
    cp -r $master/hw/hw8/* .

Your job is to implement two sorting algorithms for linked lists.  The data
structure you will use is a catenable queue.  "Catenable" means that two queues
can be concatenated into a single queue efficiently--in O(1) time.  The
LinkedQueue data structure is implemented as a singly-linked list with a tail
pointer, much like the one you worked with in Lab 3.

The LinkedQueue class (in the list package) has the following methods.

  public LinkedQueue() {
  public int size() {
  public boolean isEmpty() {
  public void enqueue(Object element) {
  public Object dequeue() throws QueueEmptyException {
  public Object front() throws QueueEmptyException {
  public void append(LinkedQueue q) {

The last method, append(), concatenates the contents of q onto the end of this
queue (in constant time), and leaves q empty.

You will implement mergesort and quicksort in the file ListSorts.java.  In
Parts I and II below, assume that the input Queue (to be sorted) contains only
Comparable elements, so that casting elements to Comparable is safe.  All
comparisons should be done using the compareTo method.  Your code should be
work with any Comparable objects, not just the Integer objects used by the test
code.  (In other words, do not cast queue elements to Integers).

The dequeue() and front() methods can throw QueueEmptyExceptions; make sure
that these exceptions are always caught.  (If your code is bug-free, such an
exception will never occur, so handle caught exceptions by simply printing an
error message.)  Do not add a "throws" clause to any method prototype that
doesn't already have one.

Part I  (4 points)
------------------
Implement mergesort on LinkedQueues using the following three steps.

(1)  Write a method called makeQueueOfQueues() that takes a LinkedQueue as
input and returns a queue of queues in which each queue contains one element
from the input queue.  For example, if the input queue is [ 3 5 2 ], this
method should return the queue [ [ 3 ] [ 5 ] [ 2 ] ].

  public static LinkedQueue makeQueueOfQueues(LinkedQueue q);

(2)  Write a method called mergeSortedQueues() that merges two sorted queues
into one.  See the comments in ListSorts.java for details.

  public static LinkedQueue mergeSortedQueues(LinkedQueue q1, LinkedQueue q2);

(3)  Implement mergeSort(), which sorts a LinkedQueue q as follows.  First,
convert q into a queue of queues using makeQueueOfQueues().  Repeatedly do the
following:  remove two elements from the queue of queues, merge them using
mergeSortedQueues(), and enqueue the result on the queue of queues.  When there
is only one queue left on the queue of queues, move its elements back to q
(preferably using the fast append() method).

  public static void mergeSort(LinkedQueue q);

Part II  (4 points)
-------------------
Implement quicksort on LinkedQueues using the following three steps.

(1)  The performance of quickSort is much more reliable if you choose a random
element from the array as your pivot.  Because we cannot access a random
location in a queue, we will instead perform a circular shift some random
number of times.  Implement a circularShift() method for this purpose.  See
the comments in ListSorts.java for details.

  public static void circularShift(LinkedQueue q, int shiftAmount);

(2)  Implement a partition() method that partitions a queue into three separate
queues containing elements less than, equal to, or greater than the pivot.
Again, see the comments for details.

  public static void partition(LinkedQueue qIn, Comparable pivot,
                               LinkedQueue qSmall, LinkedQueue qEquals,
                               LinkedQueue qLarge);

(3)  Implement quickSort(), which sorts a LinkedQueue q as follows.  Choose a
random integer between 0 and q.size() - 1.  Circularly shift q by this amount.
Dequeue what is now the first element of q, and use this as the pivot in a call
to partition().  Recursively quickSort() the smaller and larger partitions, and
then put all of the elements back into q in sorted order by using the append()
method.

  public static void quickSort(LinkedQueue q);

Part III  (1 point)
-------------------
Uncomment the timing code in the main() method, then run your sorting routines
on larger lists.  By changing the SORTSIZE constant, you may change the size of
the queues you sort.  What are the running times (in milliseconds) for sorting
lists of sizes 10, 100, 1000, and 10,000?  Also try 100,000 if your code can do
it.  Put your answers in the GRADER file.

Part IV  (1 point)
------------------
A sort is stable if items with equal keys come out in the same order they went
in.

Is your mergesort stable?  Explain why or why not.
Is your quicksort stable?  Explain why or why not.

Give a sentence or two in your explanations that describe where in your code or
in the data structures the stability of the sort is decided--that is, whether
or not equal elements are kept in their original order at critical parts of the
sorting process.  Put your answers in the GRADER file.

Submitting your solution
------------------------
Change (cd) to your hw8 directory, which should contain GRADER and
ListSorts.java.  (Your entire implementation should be in ListSorts.java.)
You're not allowed to change any other files, so you can't submit them.
Include your name, login, lab section, and lab TA's name in GRADER.  From your
hw8 directory, type "submit hw8".  You may submit as often as you like.  Only
the last version you submit before the deadline will be graded.