6. Quicksort Flashcards

1
Q

Quicksort is popular because it is

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

The basic algorithm

A

Quicksort is a divide-and-conquer method for sorting. It works by partitioning an array into two parts, then sorting the parts independently.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

The crux of the method is the partitioning process, which rearranges the array to make the following three conditions hold:

A

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].

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

We achieve a complete sort by partitioning, then recursively applying the method to the subarrays. It is a randomized algorithm, because

A

it randomly shuffles the array before sorting it.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Partitioning

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Quick.java

A

is an implementation of quicksort, using the partitioning method described above.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

//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 {

A

// This class should not be instantiated.
private Quick() { }

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

//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) {

A

StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
assert isSorted(a);
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

//Quick.java
// quicksort the subarray from a[lo] to a[hi]
private static void sort(Comparable[] a, int lo, int hi) {

A

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);
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

//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) {

A

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);
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

//Quick.java
// put partitioning item v at a[j]
exch(a, lo, j);

A

// now, a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
return j;
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

//Quick.java
/**
* Rearranges the array so that {@code a[k]} contains the kth smallest key;
*/
public static Comparable select(Comparable[] a, int k) {

A

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];
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

//Quick.java
// is v < w ?
private static boolean less(Comparable v, Comparable w) {

A

if (v == w) return false; // optimization when reference equals
return v.compareTo(w) < 0;
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

//Quick.java
// exchange a[i] and a[j]
private static void exch(Object[] a, int i, int j) {

A

Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

//Quick.java
private static boolean isSorted(Comparable[] a) {

A

return isSorted(a, 0, a.length - 1);
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

//Quick.java
private static boolean isSorted(Comparable[] a, int lo, int hi) {

A

for (int i = lo + 1; i <= hi; i++)
if (less(a[i], a[i-1])) return false;
return true;
}

17
Q

//Quick.java
// print array to standard output
private static void show(Comparable[] a) {

A

for (int i = 0; i < a.length; i++) {
StdOut.println(a[i]);
}
}

18
Q

//Quick.java
/**
* Reads in a sequence of strings from standard input; quicksorts them;
* and prints them to standard output in ascending order.
* Shuffles the array and then prints the strings again to
* standard output, but this time, using the select method.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {

A

String[] a = StdIn.readAllStrings();
Quick.sort(a);
show(a);
assert isSorted(a);

    // shuffle
    StdRandom.shuffle(a);

    // display results again using select
    StdOut.println();
    for (int i = 0; i < a.length; i++) {
        String ith = (String) Quick.select(a, i);
        StdOut.println(ith);
    }
}

}

19
Q

Implementation details:
Partitioning inplace.

A

If we use an extra array, partitioning is easy to implement, but not so much easier that it is worth the extra cost of copying the partitioned version back into the original.

20
Q

Implementation details:
Staying in bounds.

A

If the smallest item or the largest item in the array is the partitioning item, we have to take care that the pointers do not run off the left or right ends of the array, respectively.

21
Q

Implementation details:
Preserving randomness.

A

The random shuffle puts the array in random order. Since it treats all items in the subarrays uniformly, Quick.java has the property that its two subarrays are also in random order. This fact is crucial to the algorithm’s predictability. An alternate way to preserve randomness is to choose a random item for partitioning within partition().

22
Q

Implementation details:
Terminating the loop.

A

Properly testing whether the pointers have crossed is a bit trickier than it might seem at first glance. A common error is to fail to take into account that the array might contain other keys with the same value as the partitioning item.

23
Q

Implementation details:
Handling items with keys equal to the partitioning item’s key.

A

It is best to stop the left scan for items with keys greater than or equal to the partitioning item’s key and the right scan for items less than or equal to the partitioning item’s key. Even though this policy might seem to create unnecessary exchanges involving items with keys equal to the partitioning item’s key, it is crucial to avoiding quadratic running time in certain typical applications.

24
Q

Implementation details:
Terminating the recursion.

A

A common mistake in implementing quicksort involves not ensuring that one item is always put into position, then falling into an infinite recursive loop when the partitioning item happens to be the largest or smallest item in the array.

25
Q

Quicksort uses ~___ compares (and one-sixth that many exchanges) on the average to sort an array of length N with distinct keys.

A

Quicksort uses ~2 N ln N compares (and one-sixth that many exchanges) on the average to sort an array of length N with distinct keys.

26
Q

Quicksort uses ~___ compares in the worst case, but random shuffling protects against this case.

A

Quicksort uses ~N2/2 compares in the worst case, but random shuffling protects against this case.

27
Q

Improvements :
Cutoff to insertion sort.

A

As with mergesort, it pays to switch to insertion sort for tiny arrays. The optimum value of the cutoff is system-dependent, but any value between 5 and 15 is likely to work well in most situations.

28
Q

Improvements :
Median-of-three partitioning.

A

A second easy way to improve the performance of quicksort is to use the median of a small sample of items taken from the array as the partitioning item. Doing so will give a slightly better partition, but at the cost of computing the median. It turns out that most of the available improvement comes from choosing a sample of size 3 (and then partitioning on the middle item).