1、概念
快速排序(Quick Sort)的基本思想是:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录关键字小,则可分别对这两部分记录继续进行排序,已到达整个序列有序的目的。
2、算法介绍
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用中间的数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j -- ),找到第一个小于key的值A[j],A[i]与A[j]交换;
4)从i开始向后搜索,即由前开始向后搜索(i ++ ),找到第一个大于key的A[i],A[i]与A[j]交换;
5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后令循环结束。
3、code
/*对顺序表L作快速排序*/
void QSort(SqList *L,int low,int high)
{
int pivot;
if(low<high)
{
pivot=Partition(L,low,high); /*将L->r[low…high]一分为二,算出枢轴pivot*/
QSort(L,low,pivot-1);
QSort(L,pivot+1,hight);
}
}
/*交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置*/
/*此时在它之前(后)的记录均不大(小)于它*/
/*把L->r[low]放在合适的位置,并返回这个位置*/
int Partition(SqList *L,int low,int high)
{
int pivotkey;
pivotkey=L->[low]; /*用于表的第一个记录作枢轴记录*/
while(low<high) /*从标的两端交替向中间扫描*/
{
while(low<high && L->r[high]>=pivotkey)
high--;
swap(L,low,high); /*将此枢轴记录小的记录交换到低端*/
while(low<high && ->r[low]<=pivotkey)
low++;
swap(L,low,high); /*将此枢轴记录大的记录交换到高端*/
}
return low; /*返回枢轴所在位置*/
}
4、快速排序优化
(1)优化选取枢轴
如果选取的pivotkey处于整个序列中间位置,则整个排序的效率较高,但是如果是最小或最大的,则整个排序效率较低。
优化方案:三数取中法(median-of-three),即取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。
(2)优化不必要的交换
(3)优化小数组时的排序方案
快速排序适合对非常大的数组的排序,而对于规模小的数组快速排序反而不如直接插入排序更好
/*优化小数组时的排序方案:6、15、16行*/
/*优化递归操作:8、12行*/
void QSort(SqList *L,int low,int high)
{
int pivot;
if((high-low)>MAX_LENGTH_INSERT_SORT)
{
while(low<high)
{
pivot=Partition(L,low,high);
QSort(L,low,pivot-1); /*对低子表递归排序*/
low=pivot+1; /*尾递归*/
}
}
else /*当high-low小于等于常数时用直接插入排序*/
InsertSort(L);
}
/*优化选取枢轴:25-31行*/
/*优化不必要的交换:40、43、45行*/
int Partition(SqList *L,int low,int high)
{
int pivotkey;
int m=low+(high-low)/2; /*计算数组中间的元素下标*/
if(L->r[low]>L->r[high])
swap(L,low,high); /*交换左端与右端数据,保证左端较小*/
if(L->r[m]>r[high])
swap(L,hig,m); /*交换中间与右端数据,保证中间较小*/
if(L->r[m]>L->r[low])
swap(L,m,low); /*交换中间与左端数据,保证左端较小*/
/*此时L.r[low]已成为整个序列左中右三个关键字的中间值*/
pivotkey=L->[low];
L->r[0]=pivotkey; /*将枢轴关键字备份到L->r[0]*/
while(low<high)
{
while(low<high && L->r[high]>=pivotkey)
high--;
L->r[low]=L->r[high]; /*采用替换而不是交换的方式进行操作*/
while(low<high && ->r[low]<=pivotkey)
low++;
L->r[high]=L->r[low]; /*采用替换而不是交换的方式进行操作*/
}
L->r[row]=L->r[0]; /*返回枢轴所在位置*/
return low;
}