/** Array.java -- implementation of quicksort. */ import java.io.*; import java.util.*; public class Array { private int [] a; private int ops; // By default, prompt the user to enter integers on one line. // The array can be any size, so we'll use the StringTokenizer. public Array() throws IOException { System.out.print("Enter values for array: "); BufferedReader kbd = new BufferedReader(new InputStreamReader(System.in)); String line = kbd.readLine(); // We have a choice. Use an ArrayList, or tokenize the line twice. // To make the sorting algorithms simpler to read, and faster, I chose // the array implementation. In realistic Java applications, we would // probably opt for the ArrayList or some more sophisticated data // structure. StringTokenizer tok = new StringTokenizer(line, " "); int numValues = 0; while (tok.hasMoreTokens()) { tok.nextToken(); ++numValues; } a = new int [numValues]; tok = new StringTokenizer(line, " "); for (int i = 0; i < numValues; ++i) a[i] = Integer.parseInt(tok.nextToken()); //System.out.println("You entered: " + this); } public Array(Array otherArray) { int length = otherArray.a.length; a = new int[length]; for (int i = 0; i < length; ++i) a[i] = otherArray.a[i]; } public void mergeSortStarter() { ops = 0; mergeSort(a, 0, a.length - 1); } // Merge sort and Quick sort are based on the idea of "divide, conquer // and combine" public void mergeSort(int [] a, int p, int r) { //System.out.println("entering mergeSort with range " + p + ".." + r); ++ops; if (p < r) { ops += 6; int q = (p + r) / 2; mergeSort(a, p, q); mergeSort(a, q + 1, r); merge(a, p, q, r); } } // For merging 2 sub-arrays, we assume that a[p..q] and a[p+1..r] // are sorted already themselves. We need to merge these elements // to form a single larger sorted sub-array a[p..r]. // Let's merge them temporarily into a new array b. // To make this merge work, we must keep track of where we are in the two // sub-arrays. This is why we have variables leftHand and rightHand, // initialized to the starting points of the sub-arrays. public void merge(int [] a, int p, int q, int r) { int i; //System.out.println("Starting merge " + p + ".." + q + " and " + // (q+1) + ".." + r); int [] b = new int[r-p+1]; ops += 2; int leftHand = p; int rightHand = q + 1; for (++ops, i = 0; ++ops>0 && i < b.length; ++ops, ++i) { // We move an element from our left hand if it's smaller or if // our right hand is finished. In our first "if" statement we test // for the condition that we should pick the number from the left // hand. This will happen if there are no more numbers on the right, // or if we still have numbers on the left, and it's smaller than // the one on the right. if ((++ops>0 && rightHand > r) || ++ops>0 && leftHand <= q && ++ops>0 && a[leftHand] < a[rightHand]) { ops += 2; b[i] = a[leftHand]; ++leftHand; //System.out.println("left hand case, b[" + i + "] = " + b[i]); } else { ops += 2; b[i] = a[rightHand]; ++rightHand; //System.out.println("right hand case, b[" + i + "] = " + b[i]); } } // now copy the temporary b array into a sub-array within a. for (++ops, i = 0; ++ops>0 && i < b.length; ++ops, ++i) { ++ops; a[p+i] = b[i]; } //System.out.println("At end of merge, array is " + this); } // The "real" quicksort function needs to know 3 things -- the array, // and the starting and ending points. These the Driver doesn't know, // so we have this wrapping function here to formally invoke quicksort. public void quickSortStarter() { ops = 0; quickSort(a, 0, a.length - 1); } public void quickSort(int [] a, int p, int r) { //System.out.println("Entering quick sort: " + p + ".." + r); ++ops; if (p < r) { ops += 4; int q = partition(a, p, r); quickSort(a, p, q); quickSort(a, q+1, r); } } /** We are given a sub-array a with a range of elements p..r, and we need * to partition it in the spirit of "divide and conquer". Let's call * the value of the first element "x". We put all elements less than * x on the left side of the sub-array, and elements larger than x on * the right. We return the location of the boundary between the low * and high regions. */ public int partition(int [] a, int p, int r) { //System.out.println("Starting partition " + p + ".." + r); ops += 3; int x = a[p]; int i = p-1; int j = r+1; while(true) { do { ops += 2; --j; } while(a[j] > x); do { ops += 2; ++i; } while(a[i] < x); ++ops; if (i < j) { ops += 3; //System.out.println("swapping elements at " + i + " and " + j); int temp = a[i]; a[i] = a[j]; a[j] = temp; } else { ++ops; //System.out.println("partition " + p + ".." + r + " returns " + j + // " array is " + this); return j; } } } public void stoogeSortStarter() { ops = 0; stoogeSort(a, 0, a.length - 1); } public void stoogeSort(int [] a, int left, int right) { ++ops; if (a[left] > a[right]) { ops += 3; int temp = a[left]; a[left] = a[right]; a[right] = temp; } ++ops; if (left + 1 < right) { ops += 9; int oneThird = (right - left + 1) / 3; stoogeSort(a, left, right - oneThird); stoogeSort(a, left + oneThird, right); stoogeSort(a, left, right - oneThird); } } // Use the built-in sort() method from the API. We can't // count its operations, but we can look at its time in ms. public void APISort() { Arrays.sort(a); } public String toString() { String build = ""; for (int i = 0; i < a.length; ++i) build += a[i] + " "; return build; } public int getOps() { return ops; } }