Fundamentals of Computer Science II (CSC-152 98S)

[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Labs] [Assignments] [Examples] [Bailey Docs] [SamR Docs] [Tutorial] [API]


Lab 9: Quicksort

Description: In this laboratory, you will be working with an implementation of quicksort to examine the running time, to test the effects of various pivot selection strategies, and to "animate" the algorithm.

Purpose: In this lab, you will gain greater knowledge of the quicksort algorithm and begin to develop some ideas on algorithm animation.

Preparation

I've prepared a simple implementation of the quicksort algorithm appropriate for Comparable objects. Make a copy of that class and my test class. Compile both and repair any errors that you may observe.

Experimentation

The test program is intended to sort strings while illustrating what is happening during the sorting. You may want to read the underlying code to make sure it all looks correct.

You can test the quicksort algorithm by running
% ji Quicksort str1 str2 str3 ... strn
(filling in the strings of your choice). The output will illustrate the steps used in quicksort, including pivoting and recursive sorting of subvectors. If you need help interpreting the output, please ask me.

Exercise

At present, the testing routine is rather primitive. It might be appropriate to test with larger inputs (that we'd prefer not to generate by hand) and it doesn't really support automatic testing.

Write a testing routine that uses java.util.Random and rebelsky.util.ComparableInteger to test the quickSort routine on larger arrays.

Note that I make no guarantee that any of my code is correct.


Sample Code

QuicksortableVector.java



import java.util.Vector;		// These are extended vectors
import rebelsky.util.Comparable;	// We need to compare elements
import rebelsky.util.IncomparableException;
					// Some things can't be compared
import rebelsky.util.StringUtils;	// For the "animated" version
import rebelsky.io.SimpleOutput;	// For the "animated" version

/**
 * An extension of java.util.Vector that supports an
 * additional quicksort method that sorts the current
 * vector.  Written as part of a laboratory for CS152.
 * Copyright (c) 1998 Samuel A. Rebelsky and YOUR NAME HERE. 
 * All rights reserved.
 *
 * @author Samuel A. Rebelsky
 * @author YOUR NAME HERE
 * @version 1.0 of March 1998
 */
public class QuicksortableVector
  extends Vector
{

  // +-----------+---------------------------------------------------------
  // | Variables |
  // +-----------+

  /**
   * The number of steps in the last call to quicksort.
   */
  protected int steps;

  /**
   * Where does output go?  If this is not set to null, produces
   * an "animation" of sorting to the output channel.  Otherwise,
   * does not produce any output when sorting.
   */
  SimpleOutput out;

  /**
   * The width of the widest entry in the vector.  Used only when
   * printing the animation.
   */
  int width;


  // +--------------+------------------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Create a new empty vector.
   */
  public QuicksortableVector()
  {
    super();
    out = null;
    width = -1;
  } // QuicksortableVector()

  /**
   * Construct an empty vector with the specified initial capacity.
   */
  public QuicksortableVector(int initial_capacity)
  {
    super(initial_capacity);
    out = null;
    width = -1;
  } // QuicksortableVector(int)

  /**
   * Construct a new vector with specified initial capacity and
   * capacity increment. 
   */
  public QuicksortableVector(int initial_capacity, int increment)
  {
    super(initial_capacity, increment);
    out = null;
    width = -1;
  } // QuicksortableVector


  // +------------------+--------------------------------------------------
  // | Exported Methods |
  // +------------------+

  /**
   * Sort the current vector.
   * pre: All the elements of the current vector are Comparable
   *          and can be compared to each other.
   * post: The elements are in order.
   *
   * @exception IncomparableException
   *   when two elements can't be compared.
   */
  public void quickSort()
    throws IncomparableException
  {
    // Indicate that we've spent 0 steps on this process.
    steps = 0;
    // No "animation"
    this.out = null;
    // Quicksort all the elements of the vector
    quickSort(0, size()-1);
  } // quickSort()

  /**
   * Sort the current vector.  Describe the steps as we go to the
   * given output.  See the other version for pre and postconditions.
   */
  public void quickSort(SimpleOutput out)
    throws IncomparableException
  {
    // Indicate that we've spent 0 steps on this process.
    steps = 0;
    // Set up output
    this.out = out;
    // Determine the maximum width of any cell
    for (int i = 0; i < size(); ++i) {
      int new_width = elementAt(i).toString().length();
      if (width < new_width) {
        width = new_width;
      }
    } // for
    width = width + 2;
    // Quicksort all the elements of the vector
    quickSort(0, size()-1);
    // Print the sorted vector
    printSubvector(0, size()-1);
    out.println();
    // And restore the animation stuff
    out = null;
    width = -1;
  } // QuicksortableVector


  // +---------------+-----------------------------------------------------
  // | Local Methods |
  // +---------------+

  /**
   * Partition a subvector vector based on a pivot (which must be
   * in the subvector.  While determining that the pivot is at
   * position P, this method ensures that all the elements smaller
   * than or equal to the pivot precede the pivot, and all elements
   * larger than the pivot follow the pivot.
   * pre: 0 <= lb <= ub < size()
   * post: for all i, lb <= i <= P, for all j, P < j <= ub,
   *    elementAt(i) <= elementAt(j)
   * @exception IncomparableException
   *   When two elements can't be compared.
   */
  protected int partition(Comparable pivot, int lb, int ub)
    throws IncomparableException
  {
    /*
     * We'll use two indices into the subvector, one to the
     * "larger" half, one to the "smaller" half.  Both begin
     * at the far end of their presumed section.  We advance
     * each toward the center over correctly placed elements
     * until they cross or hit incorrectly placed elements.
     * When both are over incorrectly placed elements, we swap.
     */
    // The index of the pivot
    int pivot_loc = -1;
    // The index into the larger elements.
    int larger = ub;
    // The index into the smaller elements.
    int smaller = lb;
    // Keep going until we cross.  Throw an exception if we ever hit
    // an invalid type.
    try {
      while (smaller < larger) {
        // Move the larger index towards the middle as much as is possible.
        // The casting of the element to Comparable is caught outside the
        // while loop.
        while ( (larger >= lb) &&
                (pivot.lessThan(((Comparable) elementAt(larger)))) ) {
          larger = larger - 1;
        } // while ( pivot < elementAt(larger) )
        // Move the smaller index towards the middle as much as possible.
        while ( (smaller <= ub) &&
                (!pivot.lessThan(((Comparable) elementAt(smaller)))) ) {
          if (pivot.equals(elementAt(smaller))) {
            pivot_loc = smaller;
          }
          smaller = smaller + 1;
        } // while ( pivot >= elementAt(smaller) )
        // Swap if necessary
        if (smaller < larger) {
          swap(smaller,larger);
        } // if
      } // while (smaller < larger)
    } // try
    catch (ClassCastException e) {
      throw new IncomparableException();
    } // catch

    // At this point, we know that smaller points to the beginning of
    // the large element and larger points to the end of the small
    // elements.  We want to put the pivot at that position and then
    // return that position.
    swap(pivot_loc, larger);
    return larger;
  } // partition(Comparable, int, int)
 
  /**
   * Pick a pivot.  Analyses I've seen suggest that the second
   * element works better than the first.  The use of a separate
   * method lets us experiment.
   * pre: 0 <= lb < ub < size()
   * post: some element in the range [lb .. ub] is returned
   *
   * @exception IncomparableException
   *   when the selected pivots is not Comparable
   */
  protected Comparable pickPivot(int lb, int ub)
    throws IncomparableException
  {
    try {
      return (Comparable) elementAt(lb+1);
    }
    catch (Exception e) {
      throw new IncomparableException();
    }
  } // pickPivot(int, int)

  /**
   * Print a subvector.  Does not indent by the appropriate amount.
   */
  protected void printSubvector(int lb, int ub) {
    // If there are no cells, stop
    if (ub < lb) return;
    // Print an intitial separator
    out.print("|");
    // Print all the cells followed by a separator
    for (int i = lb; i <= ub; ++i) {
      out.print(StringUtils.center(elementAt(i).toString(), width) + "|");
    }
  } // printSubvector

  /**
   * Sort a subrange of the current vector (all elements between
   * lb and ub inclusive).
   * pre: All the elements in the subrange are Comparable
   *          and can be compared to each other.
   * pre: 0 <= lb, ub < size()
   * post: The elements in the range [lb .. ub] are in order.
   *
   * @exception IncomparableException
   *   when two elements can't be compared.
   */
  protected void quickSort(int lb, int ub)
    throws IncomparableException
  {
    // Observe that we've spent an additional step
    steps = steps + 1;
    // If we're animating, print the vector
    if ((out != null) && (lb <= ub)) {
      out.print(StringUtils.spaces((width+1)*lb));
      printSubvector(lb, ub);
      out.println();
    }
    // Base case: subrange of length 0 or 1.
    if (ub <= lb) return;
    // Identify a pivot.  This pivot must be in the vector.
    Comparable pivot = pickPivot(lb,ub);
    // Partition the subvector based on that pivot.  This pivot must
    // be in the vector.  The function returns the position of the
    // pivot.
    int pivot_loc = partition(pivot, lb, ub);
    // If we're animating, print the partitioned vector
    if (out != null) {
      out.print(StringUtils.spaces((width+1)*pivot_loc+1));
      out.print("*");
      out.print(StringUtils.center(elementAt(pivot_loc).toString(), width-2));
      out.print("*");
      out.println();
    } // if we're animating
    // Sort the two subvectors.  It's important that we be able to
    // shrink at least one of the subvectors by at least one element,
    // otherwise we may never stop (e.g., when all the elements are
    // the same).
    quickSort(lb, pivot_loc-1);
    quickSort(pivot_loc+1,ub);
    // No need to merge, as we've been doing everything in place
  } // quickSort(int,int)

  /**
   * Swap two elements of the vector.
   * pre: 0 <= alpha, beta < size()
   * post: the element at alpha and the element at beta have
   *     swapped positions.
   */
  protected void swap(int alpha, int beta)
  {
    Object tmp = elementAt(alpha);
    setElementAt(elementAt(beta), alpha);
    setElementAt(tmp, beta);
  } // swap(int, int)

} // QuicksortableVector



Quicksort.java



import rebelsky.io.SimpleOutput;
import rebelsky.util.ComparableString;

/**
 * A simple test of the animated quicksort routines provided by
 * QuicksortableVector.java.
 * Copyright (c) 1998 Samuel A. Rebelsky.  All rights reserved.
 *
 * @author Samuel A. Rebelsky
 * @verstion 1.0 of March 1998
 */
public class Quicksort
{
  public static void main(String[] args)
    throws Exception
  {
    // Oh boy!  Output!
    SimpleOutput out = new SimpleOutput();
    // The sortable vector.
    QuicksortableVector stuff = new QuicksortableVector();
    // Verify the number of parameters
    if (args.length == 0) {
      out.println("Usage: ji Quicksort string1 string2 ... stringn");
      System.exit(1);
    } // if
    // Append all the parameters
    for (int i = 0; i < args.length; ++i) {
      stuff.addElement(new ComparableString(args[i]));
    } // for
    // And sort!
    stuff.quickSort(out);
  } // main
} // Quicksort




[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Labs] [Assignments] [Examples] [Bailey Docs] [SamR Docs] [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.

Source text last modified Thu Mar 5 21:55:42 1998.

This page generated on Thu Mar 5 22:06:12 1998 by SiteWeaver.

Contact our webmaster at rebelsky@math.grin.edu