import Array; import Comparator; import IncomparableException; import Sortable; import java.util.Random; /** * 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.3 of October 1999 */ public class AltQuicksortable extends Array implements Sortable { // +--------+-------------------------------------------------- // | Fields | // +--------+ /** A random number generator for use in picking pivots. */ Random generator; // +--------------+-------------------------------------------- // | Constructors | // +--------------+ /** * Build a new sortable array of a particular size. */ public AltQuicksortable(int n) { super(n); generator = new Random(); } // AltQuicksortable(int) /** * Build a new sortable array with a particular set of elements. */ public AltQuicksortable(Object[] elements) { super(elements); generator = new Random(); } // AltQuicksortable(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. * * Based on the partition method given on pp. 54-55 of the * 31 March 1999 draft of Java Plus Data Structures by Nell Dale, * Sam Rebelsky, and Chip Weems. Changes are marked with CHANGED. * The most common change was to use get(i) instead of A[i]. Since * our comparators lack lessEqual, I needed to implement that with * both (lessThan(..) or equal(..)). At Nell's urging, I also * switched preincrement and predecrement (++x and --x) to * postincrement and postdecrement (x++ and x--). I also changed * the swap for last and first to eliminate the test. I used * this.swap rather than swap. I added braces around the bodies of * loops to make them easier to read. Finally, since I was required * to use slightly different parameters, I did so. * * 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 { // Our pivot, used to determine "smaller" and "larger" elements. Object pivot = this.get(lb); // CHANGED System.err.println("Pivot: " + pivot); // Elements get(lb) .. get(first) are <= pivot. Since // CHANGED // the pivot is at the front, we can set first to lb. int first = lb; // Elements get(last+1) .. get(ub) are > pivot. Since // CHANGED // we know nothing about the contents of the array, we set // last to the end. int last = ub; // Keep going until we run out of out-of-place elements. while (first < last) { // COMMENTS DROPPED // Skip over big elements on the right. // CHANGED while ( (compare.lessThan(pivot, get(last))) && // CHANGED (first < last) ) { // CHANGED System.err.println("Skipping large element: " + get(last)); last--; // CHANGED } // while there are big elements on the right // ADDED // COMMENTS DROPPED // Skip over small elements on the left. // CHANGED while ( ( compare.lessThan(get(first), pivot) // CHANGED || compare.equals(get(first), pivot) ) // CHANGED && (first < last) ) { System.err.println("Skipping small element: " + get(first)); first++; } // while there are small elements on the left // ADDED // COMMENTS DROPPED. NEW COMMENT FOLLOWS // What do we know? We stopped the second loop when // we hit a larger element or when first equaled last. // In either case, we can safely swap first and last. System.err.println("Swapping " + get(first) + " and " + get(last)); this.swap(first,last); // CHANGED } // while // COMMENTS DROPPED. // The element at postion first is <= pivot. // CHANGED // Hence, we can safely swap it and the pivot so // CHANGED // that the pivot is in the middle. // CHANGED this.swap(lb,first); // CHANGED // Return the location of the pivot. 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) { // Pick a random number. int pivloc = generator.nextInt(); // Make it positive. pivloc = Math.abs(pivloc); // Reduce it to 0 .. ub-lb. pivloc = pivloc % (1+ub-lb); // Scale it to lb .. ub. pivloc = pivloc + lb; // That's it, we're done. return pivloc; } // pickPivot(int,int) } // AltQuicksortable