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) * @author Mable Mathemagician * @author Myron Mathemagician * @version 1.0 of October 1999 */ public class Quicksortable extends Array implements Sortable { // +--------------+-------------------------------------------- // | Constructors | // +--------------+ /** * Build a new sortable array of a particular size. */ public Quicksortable(int n) { super(n); } // Quicksortable(int) /** * Build a new sortable array with a particular set of elements. */ public Quicksortable(Object[] elements) { super(elements); } // Quicksortable(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). */ public int partition(int lb, int ub, Comparator compare) throws IncomparableException { // Use the first element of the subsequence as the pivot value. int pivot = lb; int first = lb; // Elements [lb..first] are all <= pivotval int last = ub; // Elements [last+1..ub] are all > pivotval // Keep going until we run out of elements to put in the correct place. while (first < last) { // At this point, we know that // (1) first < last // (2) Elements [lb..first] are all <= pivot // (3) Elements [last+1..ub] are all > pivot // Skip over any large elements in the right half while ( (compare.lessThan(this.get(pivot),this.get(last))) && (first < last)) --last; // At this point, we know that // (1) first <= last (we stop moving r left when we hit first // or run out of large elements) // (2) elements [lb..first] are all <= pivot (we haven't moved first) // (3) elements [last+1..ub] are all > pivot (by the for loop) // (4) element last is <= pivot (we either stopped moving when // we hit such an element or (a) last = first and (b) first // indexes such an element) // Skip over any small elements in the left half. while ( ((compare.lessThan(this.get(first),this.get(pivot))) || (compare.equals(this.get(first),this.get(pivot)))) && (first < last)) ++first; // At this point, we know that // (1) first <= last (we stop moving last left when we hit first or // possibly sooner; we stop moving first right when we hit // last or possibly sooner) // (2) elements [lb..first-1] are all <= pivot (by the for loop) // (3) elements [last+1..ub] are all > pivot (we haven't moved last) // (4) element last is <= pivot (we either stopped moving when // we hit such an element or last = first (and first indexes // such an element) // (5) if first < last then element first is > pivot // (by the for loop) // (6) if first = last then element first is <= pivot // Do we have a large element in the left and a small element // on the right? if (compare.lessThan(this.get(last),this.get(first))) this.swap(first,last); }//while // At this point, we know that // (1) elements [lb..first] are all <= pivot // (2) elements [first+1..ub] are all > pivot // Put the pivot in the middle. Note that at this point, element first is this.swap(lb,first); // Done! return first; }// 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(int,int) } // Quicksortable