1.快速排序基本思想
通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,达到整个序列有序。
2.快速排序算法的实现
//对顺序表L作快速排序
void QuickSort(SqList *L)
{
QSort(L,1,L->length);
}
//将顺序表L的子序列L->r[low..high]作快速排序
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,high); //对高子表递归排序
}
}
pivot称为枢轴,表示一个位置,该位置的特点是:该位置左边的值都比它小,右边的值都比它大,这个特点就是快速排序算法中最关键的Partition函数要实现的功能。
//交换顺序表L中子表的记录,返回枢轴记录所在的位置
int Partition(SqList *L,int low,int high)
{
int PivotKey;
PivotKey=L->r[low]; //用子表的第一个记录作枢轴记录
while (low<high) //从表的两端交替向中间扫描
{
while ( low<high && L->r[high]>=PivotKey )
{
high--;
}
swap(L,low,high); //将比枢轴记录小的记录交换到低端
while ( low<high && L->r[low]<=PivotKey )
{
low++;
}
swap(L,low,high); //将比枢轴记录大的记录交换到高端
}
return low; //返回枢轴所在位置
}
3.快速排序优化
(1)优化选取枢轴
枢轴太大或者太小,都会影响性能。选取三数取中法选取枢轴,即取三个关键字先进行排序,将中间数作为枢轴,一般取左端、中间、右端三个数。修改Partition函数为:
int Partition(SqList *L,int low,int high)
{
int PivotKey;
//新增部分:保证交换之后l.r[low]为左中右三个关键字的中间值
int mid=low+(high-low)/2;
if (L->r[low] > L->r[high])
{
swap(L,low,high);
}
if (L->r[mid] > L->r[low])
{
swap(L,low,mid);
}
if (L->r[mid] > L->r[high])
{
swap(L,mid,high);
}
//新增部分
PivotKey=L->r[low]; //用子表的第一个记录作枢轴记录
while (low<high) //从表的两端交替向中间扫描
{
while ( low<high && L->r[high]>=PivotKey )
{
high--;
}
swap(L,low,high); //将比枢轴记录小的记录交换到低端
while ( low<high && L->r[low]<=PivotKey )
{
low++;
}
swap(L,low,high); //将比枢轴记录大的记录交换到高端
}
return low; //返回枢轴所在位置
}
(2)优化不必要的交换
int Partition(SqList *L,int low,int high)
{
int PivotKey;
//新增部分:保证交换之后l.r[low]为左中右三个关键字的中间值
int mid=low+(high-low)/2;
if (L->r[low] > L->r[high])
{
swap(L,low,high);
}
if (L->r[mid] > L->r[low])
{
swap(L,low,mid);
}
if (L->r[mid] > L->r[high])
{
swap(L,mid,high);
}
//新增部分
PivotKey=L->r[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 && L->r[low]<=PivotKey )
{
low++;
}
L->r[high]=L->r[low]; //改变部分
}
L->r[low]=L->r[0]; //新增部分
return low; //返回枢轴所在位置
}
(3)优化小数组时的排序方案
#define MAX_LENGTH_INSERT_SORT 7
void QSort(SqList &L,int low,int high)
{
int pivot;
if ((high-low)>MAX_LENGTH_INSERT_SORT) //当大于常数时用快速排序
{
pivot=Partition(L,low,high); //将L->r[low..high]一分为二
//计算出枢轴值pivot
QSort(L,low,pivot-1); //对低子表递归排序
QSort(L,pivot+1,high); //对高子表递归排序
}
else
{
InsertSort(L); //当小于常数时用直接插入排序法
}
}
(4)优化递归操作
每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归耗费的空间就越多。对QSort实施尾递归优化。c++里快速排序用qsort()。
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); //将L->r[low..high]一分为二
//计算出枢轴值pivot
QSort(L,low,pivot-1); //对低子表递归排序
low = pivot+1; //尾递归
}
}
else
{
InsertSort(L); //当小于常数时用直接插入排序法
}
}