6. Quicksort Flashcards
Quicksort is popular because it is
not difficult to implement, works well for a variety of different kinds of input data, and is substantially faster than any other sorting method in typical applications. It is in-place (uses only a small auxiliary stack), requires time proportional to N log N on the average to sort N items, and has an extremely short inner loop.
The basic algorithm
Quicksort is a divide-and-conquer method for sorting. It works by partitioning an array into two parts, then sorting the parts independently.
The crux of the method is the partitioning process, which rearranges the array to make the following three conditions hold:
The entry a[j] is in its final place in the array, for some j.
No entry in a[lo] through a[j-1] is greater than a[j].
No entry in a[j+1] through a[hi] is less than a[j].
We achieve a complete sort by partitioning, then recursively applying the method to the subarrays. It is a randomized algorithm, because
it randomly shuffles the array before sorting it.
Partitioning
First, we arbitrarily choose a[lo] to be the partitioning item—the one that will go into its final position. Next, we scan from the left end of the array until we find an entry that is greater than (or equal to) the partitioning item, and we scan from the right end of the array until we find an entry less than (or equal to) the partitioning item.
The two items that stopped the scans are out of place in the final partitioned array, so we exchange them. When the scan indices cross, all that we need to do to complete the partitioning process is to exchange the partitioning item a[lo] with the rightmost entry of the left subarray (a[j]) and return its index j.
Quick.java
is an implementation of quicksort, using the partitioning method described above.
//Quick.java
/**
* The {@code Quick} class provides static methods for sorting an
* array and selecting the ith smallest element in an array using quicksort.
*/
public class Quick {
// This class should not be instantiated.
private Quick() { }
//Quick.java
/**
* Rearranges the array in ascending order, using the natural order.
* @param a the array to be sorted
*/
public static void sort(Comparable[] a) {
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
assert isSorted(a);
}
//Quick.java
// quicksort the subarray from a[lo] to a[hi]
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int j = partition(a, lo, hi);
sort(a, lo, j-1);
sort(a, j+1, hi);
assert isSorted(a, lo, hi);
}
//Quick.java
// partition the subarray a[lo..hi] so that a[lo..j-1] <= a[j] <= a[j+1..hi]
// and return the index j.
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo;
int j = hi + 1;
Comparable v = a[lo];
while (true) {
// find item on lo to swap while (less(a[++i], v)) { if (i == hi) break; } // find item on hi to swap while (less(v, a[--j])) { if (j == lo) break; // redundant since a[lo] acts as sentinel } // check if pointers cross if (i >= j) break; exch(a, i, j); }
//Quick.java
// put partitioning item v at a[j]
exch(a, lo, j);
// now, a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
return j;
}
//Quick.java
/**
* Rearranges the array so that {@code a[k]} contains the kth smallest key;
*/
public static Comparable select(Comparable[] a, int k) {
if (k < 0 || k >= a.length) {
throw new IllegalArgumentException(“index is not between 0 and “ + a.length + “: “ + k);
}
StdRandom.shuffle(a);
int lo = 0, hi = a.length - 1;
while (hi > lo) {
int i = partition(a, lo, hi);
if (i > k) hi = i - 1;
else if (i < k) lo = i + 1;
else return a[i];
}
return a[lo];
}
//Quick.java
// is v < w ?
private static boolean less(Comparable v, Comparable w) {
if (v == w) return false; // optimization when reference equals
return v.compareTo(w) < 0;
}
//Quick.java
// exchange a[i] and a[j]
private static void exch(Object[] a, int i, int j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
//Quick.java
private static boolean isSorted(Comparable[] a) {
return isSorted(a, 0, a.length - 1);
}