import java.util.Random; /** * Sequences of integers that support sorting. * * @author Samuel A. Rebelsky * @version 1.1 of September 1998 */ public class SortableIntSeq { // +--------+-------------------------------------------------- // | Fields | // +--------+ /** * The elements in the sequence (plus some spare spaces * so that we can easily add more elements). */ protected int[] elements; // +---------------+------------------------------------------- // | Basic Methods | // +---------------+ /** * Get the value of the ith element of the sequence. * Returns the value of the ith element. If a negative * index is given, return 0. */ public int get(int i) { // Check for negative indices if (i < 0) { return 0; } // negative index // Check for indices outside the range of the array else if ((this.elements == null) || (i >= this.elements.length)) { return 0; } // really large index // Everything else else { return this.elements[i]; } // everything else } // get(int) /** * Get the ``length'' of the sequence. In general, this is one * higher than the index of the highest element accessed. */ public int length() { if (this.elements == null) { return 0; } else { return this.elements.length; } } // length() /** * Convert the sequence to a string. */ public String toString() { // Handle empty sequences if (this.elements.length == 0) { return "[]"; } // Handle nonempty sequences else { int i; // A counter String str; // The string we generate str = "[" + this.elements[0]; for (i = 1; i < this.elements.length; ++i) { str = str + "," + this.elements[i]; } // for str = str + "]";; return str; } // nonempty sequence } // toString() /** * Set the value of the ith element of the sequence. * Returns the old ith element. If a negative index * is given, does not affect the sequence and returns 0. */ public int set(int i, int val) { // Check for negative indices if (i < 0) { return 0; } // Make sure the array is big enough verifyCapacity(i+1); // Get the old value int old = elements[i]; // Fill in the new value elements[i] = val; // Return the old value return old; } // set(int,int) /** * Swap the values at positions i and j. */ public void swap(int i, int j) { int tmp; // A temporary value used in swapping // Make sure there are sufficiently many elements verifyCapacity(j+1); // Swap by copying tmp = this.elements[i]; this.elements[i] = this.elements[j]; this.elements[j] = tmp; } // swap(int,int) // +---------------------+------------------------------------- // | Fill in in elements | // +---------------------+ /** * Fill the subsequence [lb .. ub] with the sequence of elements * starting with one value and incrementing by inc at every step. For * example, fill(2,5,3,4) puts 3 in position 2, 7 in position 3, * 11 in position 4, and 15 in position 5. */ public void fill(int lb, int ub, int start, int inc) { int current; // The current value being inserted int i; // A counter // Make sure the array is big enough verifyCapacity(ub+1); // Set the initial value current = start; // Fill in the values for (i = lb; i <= ub; ++i) { this.elements[i] = current; current += inc; } // for } // fill(int,int,int,int) /** * Fill the first n elements of a sequence with n random elements. */ public void randomFill(int n) { randomFill(0, n-1, 0); } // randomFill(int) /** * Fill the subsequence [lb ..ub] with the kth sequence * of ``random'' numbers between -1000 and 1000. If k is * 0, uses an unspecified sequence of ``random'' elements. */ public void randomFill(int lb, int ub, int k) { randomFill(lb, ub, k, 1000); } // randomFill(int, int, int) /** * Fill the subsequence [lb .. ub] with the kth sequence * of ``random'' elements between -cap and cap. If k is 0, * an unspecified sequence of ``random'' elements is used. */ public void randomFill(int lb, int ub, int k, int cap) { int i; // Counter variable Random gen; // A random sequence generator // Set up the random sequence generator if (k == 0) { gen = new Random(); } else { gen = new Random(k); } // Make sure the array has sufficient capacity verifyCapacity(ub+1); // Fill it with random elements (not too large) for (i = lb; i <= ub; ++i) { this.elements[i] = gen.nextInt() % cap; } // for } // randomFill(int,int,int,int) // +------------------+---------------------------------------- // | Smallest Methods | // +------------------+ /** * Compute the index of the smallest element in the subsequence * given by lower bound lb and upper bound ub. The subsequence * [lb .. ub] must be nonempty. */ public int indexOfSmallest(int lb, int ub) { // Make sure there are elements in the sequence if (this.elements == null) { return Integer.MIN_VALUE; } // Make sure the upper bound and lower bound are reasonable. if (lb < 0) { lb = 0; } if (ub >= this.elements.length) { ub = this.elements.length - 1; } // Our guess as to the index of the smallest element int guess = lb; // A counter variable int i; // Look through all subsequent elements for (i = lb + 1; i <= ub; ++i) { // If the element is smaller than our guess, then // update the guess if (this.elements[i] < this.elements[guess]) { guess = i; } // if } // for // That's it, we're done return guess; } // indexOfSmallest(int) /** * Compute the smallest element in the sequence. The sequence * must be nonempty. */ public int smallest() { // Our guess as to the smallest element int guess = this.elements[0]; // A counter variable int i; // Look through all subsequent elements for (i = 1; i < this.elements.length; ++i) { // If the element is smaller than our guess, then // update the guess if (this.elements[i] < guess) { guess = this.elements[i]; } // if } // for // That's it, we're done return guess; } // smallest() /** * Put the two smallest elements of the sequence at the beginning * of the sequence. The sequence must have at least two elements. */ public void twoSmallest() { // Swap the initial element with the smallest swap(0, indexOfSmallest(0,this.length()-1)); // Swap the next element with the smallest remaining swap(1, indexOfSmallest(1,this.length()-1)); } // twoSmallest() /** * Put the five smallest elements of the array at the beginning of * the array (naive method). The sequence should have at least * five elements. */ public void fiveSmallest() { // For each index i from 0 to 4, swap the smallest element in // [i .. last] with the ith element. int i; for (i = 0; i < 5; ++i) { swap(i, indexOfSmallest(i, this.length()-1)); } // for } // fiveSmallest() /** * Put the five smallest elements of the array at the beginning of * the array (better method). The sequence should have at least * five elements. */ public void newFiveSmallest() { int i; // Everybody's favorite counter variable // Put the first five elements in order using the naive method for (i = 0; i < 4; ++i) { swap(i, indexOfSmallest(i, 4)); } // for // For each remaining element, if it belongs in the first five // (or, more precisely, our estimate of the first five), then // put it in the correct place in that sorted sublist. for (i = 5; i < this.length(); ++i) { // Does the remaining element belong in our guess about the // first five elements? if (this.elements[i] < this.elements[4]) { // If so, put it at the end of that group swap(4, i); // And then move it to the right place in that group insertLast(0,4); } // if the ith element belongs in our current guess } // for } // newFiveSmallest() // +-----------------+----------------------------------------- // | Sorting Methods | // +-----------------+ /** * Sort the current sequence using the legendary insertion * sort algorithm. */ public void insertionSort() { int i; // For each prefix subsequence for (i = 1; i < this.length(); ++i) { // Sort that prefix subsequence insertLast(0, i); } // for } // insertionSort() /** * Sort the current sequence using the legendary Quicksort * algorithm. */ public void quickSort() { quickSort(0, this.length()-1); } // quickSort() /** * Sort the subsequence given by [lb .. ub] using the legendary * Quicksort algorithm. */ public void quickSort(int lb, int ub) { // Base case: the subsequence has 0 or 1 elements if (ub <= lb) { return; } // Recursive case: at least two elements else { // Break the subsequence up into smaller elements and // larger elements int splitter = split(lb,ub); // Sort the smaller subsequence quickSort(lb, splitter-1); // Sort the larger subsequence quickSort(splitter+1,ub); } // recursive case } // quickSort(int,int) /** * Split the subsequence given by [lb .. ub] into smaller and * larger elements. Return the index of the pivot between those * elements. */ public int split(int lb, int ub) { // Use the first element of the subsequence as the pivot value. int pivotval = this.elements[lb]; int l=lb; // Elements [lb..l] are all <= pivotval int r=ub; // Elements [r..ub] are all > pivotval // Keep going until we run out of elements to put in the correct place while (l < r) { // At this point, we know that // (1) l < r // (2) Elements [lb..l] are all <= pivotval // (3) Elements [r..ub] are all > pivotval // Skip over any large elements in the right half while ((this.elements[r] > pivotval) && (r > l)) { --r; } // At this point, we know that // (1) l <= r (we stop moving r left when we hit l or run out // of large elements) // (2) elements [lb..l] are all <= pivotval (we haven't moved l) // (3) elements [r+1..ub] are all > pivotval (by the for loop) // (4) element r is <= pivotval (we either stopped moving when // we hit such an element or r = l and l indexes such an // element) // Skip over any small elements in the left half while ((this.elements[l] <= pivotval) && (l < r)) { ++l; } // At this point, we know that // (1) l <= r (we stop moving r left when we hit l or run out // of large elements) // (2) elements [lb..l-1] are all <= pivotval (by the for loop) // (3) elements [r+1..ub] are all > pivotval (we haven't moved r) // (4) element r is <= pivotval (we either stopped moving when // we hit such an element or r = l (and l indexes such an // element) // (5) if l < r then element l is > pivotval (by the for loop) // (6) if l = r then element l is <= pivotval // Do we have a large element in the left and a small element // on the right? if (this.elements[l] > this.elements[r]) { swap(l,r); } } // while // At this point, we know that // (1) elements [lb..l] are all <= pivotval // (2) elements [l+1..ub] are all > pivotval // Put the pivot in the middle. Note that at this point, element l is // <= pivotval, so this is a safe swap swap(lb, l); // And we're done return l; } // split /** * Sort the current sequence using the legendary selection * sort algorithm. */ public void selectionSort() { // Not yet implemented } // selectionSort() // +----------------+------------------------------------------ // | Helper Methods | // +----------------+ /** * Insert the last element in the subrange [lb .. ub] into the * appropriate place in [lb .. ub], assuming that the subrange * [lb .. ub-1] is sorted in increasing order. */ protected void insertLast(int lb, int ub) { int i; // A counter variable // Start at the upper end of the range i = ub; // As long as the ith element is out of order, while ((i > 0) && (this.elements[i] < this.elements[i-1])) { // Swap the two out-of-order elements swap(i,i-1); // Move back one element i = i - 1; } // while } // insertLast(int,int) /** * Increase the capacity of the elements array. */ protected void verifyCapacity(int newcap) { // Special case: empty array if (this.elements == null) { // Create an appropriate sized array this.elements = new int[newcap]; // Fill it with sufficiently many 0 elements int i; for (i = 0; i < this.elements.length; ++i) { this.elements[i] = 0; } // for } // if the array is empty // Normal case: too few elements else if (this.elements.length < newcap) { // Build a new array of the appropriate size int[] new_elements = new int[newcap]; // Set up a counter int j; // Copy the existing elements for (j = 0; j < this.elements.length; ++j) { new_elements[j] = this.elements[j]; } // for // Use 0 for the remaining elements for (j = this.elements.length; j < new_elements.length; ++j) { new_elements[j] = 0; } // for // And update elements this.elements = new_elements; } // if we need to increase the capacity } // verifyCapacity(int) } // class SortableIntSeq