快速排序是一个分治算法,它首先把数组分为两部分,然后对每一个部分递归的求解,但它不需要合并子问题的解。快速排序的伪代码如下:
其中,A是一个数组,p是数组中需要排序的子数组的开始下标,r是结束下标,q是一个分隔元素,PARTITION(A, p,r)返回后,q下标左边的元素都比A[q]小,q下标右边的元素都比A[q]大。这样数组就被分为两部分,一部分比A[q]小,一部分比A[q]大;而且最终确定了A[q]在数组中的位置。其实,快速排序,就是不断的确定子数组中最后一个元素在子数组中的位置,也就是不断确定该元素在整个数组中的最终位置(这一点非常重要),最终达到排序的目的。
从快速排序的伪代码中可以看到,其中的PARTITION(A, p,r)是关键,它把数组分为两部分,该函数返回的结果上面已经讲了,这里说一下实现它的思想。设置整数i表示数组中已知的小于A[r]的元素中最右边元素的位置,扫描p到r - 1的所有元素,遇到于小于A[r]的,就把该元素和A[++i]的交换,最后把A[r]和A[++i]交换。ARTITION(A, p,r)的伪代码如下:
快速排序类的Java代码如下:
public class QuickSort{
public <T extends Comparable<T>> void sort(T[] a) {
// TODO Auto-generated method stub
this.sort(a, 0, a.length - 1);
}
public <T extends Comparable<T>> void sort(T[] a, int p, int r) {
// TODO Auto-generated method stub
// 确保待排序的子数组中至少有两个元素
if (p < r) {
int q = this.partition(a, p, r);
sort(a, 0, q - 1);
sort(a, q + 1, r);
}
}
/**
* 把数组a分解为两部分,比a[r]大的那部分在a[r]的右边,
* 比a[r]小的那部分在a[r]的左边;
* 实际上就是在数组中寻找a[r]最终位置
* @param a
* @param p
* @param r
* @return
*/
private <T extends Comparable<T>> int partition(T[] a, int p, int r) {
T temp = a[r];
// i是数组中已知比a[r]小的最右边元素的位置
int i = p - 1;
for(int j = p; j <= r - 1; j++){
/**
* 如果a[j]比temp小,就应该放在a[i]的下一个位置;
* 而且应该把a[i]下一个位置的元素放在a[j]的位置上
*/
if (a[j].compareTo(temp) < 0) {
i++;
T temp2 = a[j];
a[j] = a[i];
a[i] = temp2;
}
}
i++;
a[r] = a[i];
a[i] = temp;
return i;
}
public static void main(String[] args) {
Integer[] heap = new Integer[]{0, 3, 1, 5, 12, 7};
new QuickSort().sort(heap);
for (int i = 0; i < heap.length; i++) {
System.out.println(heap[i]);
}
}
}
参考 算法导论 第七章 快速排序