快排的特点是:
以某个记录为界(该记录称为支点或枢轴),将待排序列分成两部分:
①一部分: 所有记录的关键字大于等于支点记录的关键字
②另一部分: 所有记录的关键字小于支点记录的关键字
下面通过图来描述快排,快排有两种,一种是数据结构中的,一种是算法设计中的
先介绍数据结构中快排的过程
数据结构书中介绍的快排枢轴会左右两边跑动。当枢轴在左边时,需要从序列右边找比枢轴小的数与枢轴交换位置;当枢轴在右边时,需要从序列左边找比枢轴大的数与枢轴交换位置。这种方式不好,而且枢轴两边跑动不好写代码。
下面介绍算法结构书中介绍的快排的过程
注意:在i和j交换位置后一定要注意是将j位置的数和枢轴进行交换。因为j位置的数是比枢轴小的数,与枢轴交换即j位置的数到枢轴左边去,这是合理的。而i位置的数是比枢轴大的数,交换到枢轴左边是不合理的。
算法分析书中介绍的快排算法枢轴位置不要左右两边跑动,而且一次便可确定两个数的位置,效率较高,建议在编程时采用算法分析中介绍的快排。其代码如下:
//5.1 普通快速排序
private static void quickSort(int[] a, int p, int r) {
if(p<r){
//思路:把数组a划分成两个子区间和一个元素:a[p:q-1], a[q], a[q+1:r]
int q = patition(a,p,r);//经过该划分函数,数组就变成:a[p:q-1], a[q], a[q+1:r]----其中a[q]位置已经排好
quickSort(a,p,q-1);//继续排左子区间
quickSort(a,q+1,r);//继续排右子区间
}
}
private static int patition(int[] a, int p, int r) {
int i=p;
int j=r+1;
int x=a[p];//枢轴保存在变量x中
while(true){
while(a[++i]<x && i<r);//这个循环之后,a[i]就是左区中一个大于x的元素
while(a[--j]>x);//这个循环之后,a[j]就是右区中一个小于x的元素
if(i>=j){
break;
}
swap(a,i,j);//把a[i]和a[j]交换一下
}
//把枢轴(目前在第p的位置) 交换到 j 的位置
swap(a,p,j);
return j;
}
下面介绍优化后的快排,优化后的快排与普通快排算法的区别在于:
其枢轴是通过随机决定的(随机确定序列中的一个数为枢轴,然后把这个数与序列中的第一个数交换位置),而通过快排的枢轴就是选序列中的第一个数。这种优化的好处是:快排算法是序列越无序其效率越高,若序列是一个较为有序的序列此时直接选序列中的第一个数作为枢轴,则算法效率太低。使用优化后的快排较好。。
//5.2 优化后的快速排序
private static void quickSort2(int[] a, int p, int r) {
if(p<r){
//思路:把数组a划分成两个子区间和一个元素:a[p:q-1], a[q], a[q+1:r]
int q = patition2(a,p,r);//经过该划分函数,数组就变成:a[p:q-1], a[q], a[q+1:r]----其中a[q]位置已经排好
quickSort2(a,p,q-1);//继续排左子区间
quickSort2(a,q+1,r);//继续排右子区间
}
}
private static int patition2(int[] a, int p, int r) {
//优化:采用随机选择策略确定枢轴
int rand =p+ (int)( Math.random()*(r-p));
swap(a,p,rand);//把rand位置的元素换到p位置,然后把p位置的元素当作枢轴
int i=p;
int j=r+1;
int x=a[p];//枢轴保存在变量x中
while(true){
while(a[++i]<x && i<r);//这个循环之后,a[i]就是左区中一个大于x的元素
while(a[--j]>x);//这个循环之后,a[j]就是右区中一个小于x的元素
if(i>=j){
break;
}
swap(a,i,j);//把a[i]和a[j]交换一下
}
//把枢轴(目前在第p的位置) 交换到 j 的位置
swap(a,p,j);
return j;
}