快速排序是一种分治的排序算法。它将一个数组分成两个子数组,将两部分独立地排序。快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序;而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了。在第一种情况中,递归调用发生在处理整个数组之前;在第二种情况中,递归调用发生在处理整个数组之后。在归并排序中,一个数组被等分为两半;在快速排序中,切分(partition)的位置取决于数组的内容。
快速排序递归地将子数组a[lo..hi]排序,先用partiton()方法将a[j]放到一个合适的位置,然后在用递归调用将其他位置的元素排序。切分的过程使得数组满足以下三个条件:1、对于某个j,a[j]已经排定 2、a[lo] 到 a[j-1]中的所有元素都不大于a[j] 3、a[j+1] 到 a[hi]中的所有元素都不大于a[j]
public class Quick {
public static void sort(Comparable[] a){
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
}
public 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);
}
private static int partition(Comparable[] a, int lo, int hi) {
}
private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a){
for(int i = 0; i < a.length; i++){
StdOut.print(a[i] + " ");
}
StdOut.println();
}
}
切分方法一般策略是先随意地去a[lo]作为切分元素,即那个将会被排定的元素,然后从数组的左端开始向右扫描直到找到一个大于等于它的元素,再从数组的右端开始向左扫描直到找到一个小于等于它的元素。这两个元素显然是没有排定的,因此我们交换它们的位置。如此继续,我们就可以保证左指针i的左侧元素都不大于切分元素,右指针j的右侧元素都不小于切分元素。当两个指针相遇时,我们只需将切分元素a[lo]和左子数组最右侧的元素(a[j])交换然后返回j即可。
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo, j = hi + 1;
Comparable v = a[lo];
while(true){
while(less(a[++i], v)) if(i == hi) break;
while(less(v, a[--j])) if(j == lo) break;
if(i >= j) break;
}
exch(a, i, j);
return j;
}
这段代码按照a[lo]的值v进行切分。当指针i和j相遇时主循环退出。在循环中,a[i]小于v时我们增大i,a[j]大于v时我们减小j,然后交换a[i]和a[j]来保证i左侧的元素都不大于v,j右侧的元素都不小于v。当指针相遇时交换a[lo]和a[j],切分结束(这样切分值就留在a[j]中了)