import Array; import Comparator; import IncomparableException; import Sortable; /** * Arrays that can be sorted using the amazing Quicksort routine. Note * that we violate Java's method naming conventions because Quicksort is * a proper name. * * @author Samuel A. Rebelsky (general structure, partition method) * @author Mable Mathemagician * @author Myron Mathemagician * @version 1.1 of October 1999 */ public class NewQuicksortable extends Array implements Sortable { // +--------------+-------------------------------------------- // | Constructors | // +--------------+ /** * Build a new sortable array of a particular size. */ public NewQuicksortable(int n) { super(n); } // NewQuicksortable(int) /** * Build a new sortable array with a particular set of elements. */ public NewQuicksortable(Object[] elements) { super(elements); } // NewQuicksortable(Object[]) // +-----------------------+----------------------------------- // | Methods from Sortable | // +-----------------------+ /** * Sort an array using Quicksort. * Pre: All elements in the array can be compared to each other. * Post: The array is sorted (using the standard meaning). */ public void sort(Comparator compare) throws IncomparableException { sort(compare, null); } // sort(Object[]) /** * Sort an array using Quicksort. If observer is non-null, * prints out pithy notes about the sorting. * Pre: All elements in the array can be compared to each other. * Post: The array is sorted (using the standard meaning). */ public void sort(Comparator compare, SimpleOutput observer) throws IncomparableException { Quicksort(0, size()-1, compare, observer); } // sort(Object[]) // +----------------+------------------------------------------ // | Helper Methods | // +----------------+ /** * Sort part of an array using Quicksort. * Pre: All elements in the subarray can be compared to each other. * Pre: 0 <= lb <= ub < size() * Post: The array is sorted (using the standard meaning). */ protected void Quicksort(int lb, int ub, Comparator compare, SimpleOutput observer) throws IncomparableException { if (observer != null) { observer.println("lb = " + lb + "; ub = " + ub); } // Variables Object pivot; // The pivot we select. Must be part of the subarray. int mid; // The position of the pivot // Base case: size one arrays are sorted. if (lb == ub) return; // Pick a pivot and put it at the front of the array. swap(lb,pickPivot(lb,ub)); // Determine the position of the pivot, while rearranging the array. mid = partition(lb,ub,compare); // Recurse on nonempty subarrays. if (lb<=mid-1) Quicksort(lb,mid-1, compare, observer); if (mid+1<=ub) Quicksort(mid+1,ub, compare, observer); } // Quicksort /** * Split the subarray given by [lb .. ub] into ``smaller'' and * ``larger'' elements, where smaller and larger are defined by * their relationship to a pivot. Return the index of the pivot * between those parts. Uses the first element of the array * as the pivot. * Pre: All the elements in the subarray can be compared. * Post: Returns n such that for all i between lb and n inclusive * and j between n+1 and ub inclusive, get(i) <= pivot < get(j). */ protected int partition(int lb, int ub, Comparator compare) throws IncomparableException { // Strategy: split the array into three parts: the stuff known to // be less-than-or-equal to the pivot, the stuff known to // be greater than the pivot, and the stuff of unknown // character. // Initially (and at every repetition of the main loop), // Smaller or equal stuff is in get(lb) .. get(lower) // Bigger stuff is in get(upper+1) .. get(ub) // Unknown stuff is in get(lower+1) .. get(upper) int lower = lb; int upper = ub; // Get the pivot Object pivot = this.get(lb); // Keep going until we hit the middle element. while (lower < upper) { // Some error checking. Pay no attention. // System.err.println("lower: " + lower + "; upper: " + upper); // System.err.println(" Pivot: '" + pivot.toString() + "'"); // System.err.println(" Lower: '" + get(lower).toString() + "'"); // System.err.println(" Upper: '" + get(upper).toString() + "'"); // Try to expand the upper range. Stop when you find a // small element or run out of things to look at while ( (compare.lessThan(pivot, this.get(upper))) && (lower < upper) ) { upper--; } // expand the upper range // Try to expand the lower range. Stop when you find a // large element or run out of elements. while ( ( (compare.lessThan(this.get(lower),pivot)) || (compare.equals(pivot, this.get(lower))) ) && (lower < upper) ) { lower++; } // expand the lower range. // If lower is not the same as upper, we've found a small // element in the upper range and a large element in the // smaller range, so swap 'em // of place. if (lower != upper) this.swap(lower,upper); } // while // We want to put the pivot in the middle. lower gives // the position of the last "small" element. Put the // pivot there. this.swap(lb,lower); // The pivot is now at position lower. return lower; } // partition(int, int, Comparator) /** * Pick a pivot in a subarray. * Pre: 0 <= lb <= ub < size() * Post: returns the index of the pivot (a value between lb and ub inclusive). */ protected int pickPivot(int lb, int ub) { // Simple implementation: pick the last element. Might be extended // to do something more interesting, such as pick a random element // in the subarray. return ub; } // pickPivot() } // NewQuicksortable