|
10.3.2 快速排序
一趟快排也称"一次划分",即将待排序列 R[s..t]"划分"为两个子序列R[s..i-1] 和 R[i+1..t],i
为一次划分之后的枢轴位置。可以取待排序列中任何一个记录作为枢轴,但为方便起见,通常取序列中第一个记录 R[s] 为枢轴,以它的关键字作为划分的依据。划分可如下进行:设置两个指针
low 和 high,分别指向待排序列的低端 s 和高端 t。若 R[high].key<R[s].key,则将它移动至枢轴记录之前;反之,若
R[low].key>R[s].key,则将它移动至枢轴记录之后,并为避免枢轴来回移动,可先将枢轴 R[s] 暂存在数组的闲置分量 R[0]
中。如右侧所示为"一次划分"过程的一个例子。一次划分(一趟快速排序)的算法如下:
算法 10.11
int Partition ( RcdType R[], int low, int high)
{
//
对记录子序列 R[low..high] 进行一趟快速排序,并返回枢轴记录
// 所在位置,使得在它之前的记录的关键字均不大于它的关键字,
// 而在它之后的记录的关键字均不小于它的关键字
R[0] = R[low]; //
将枢轴记录移至数组的闲置分量
pivotkey = R[low].key; //
枢轴记录关键字
while (low<high) { //
从表的两端交替地向中间扫描
while (low<high && R[high].key>=pivotkey)
--high;
R[low++] = R[high]; //
将比枢轴记录小的记录移到低端
while (low<high && R[low].key<=pivotkey)
++low;
R[high--] = R[low]; //
将比枢轴记录大的记录移到高端
} // while
R[low] = R[0]; //
枢轴记录移到正确位置
return low; //
返回枢轴位置
} // Partition
可以推证,快速排序的平均时间复杂度为O
(nlogn),在三者取中的前提下,对随机的关键字序列,快速排序是目前被认为是最好的排序方法,如果借用起泡排序中设置记录"交换与否"的布尔变量的作法,快速排序也适用于已经有序的记录序列,详情参见《数据结构题集》题10.9。
|
|
"一次划分"过程:
|
试对上例中得到的子序列
( 90, 45, 39, 54, 68, 87, 76 )
继续进行快速排序,你是否发现什么特殊情况?
|
|
|
由于枢轴记录的关键字"90"大于其它所有记录的关键字,致使一次划分之后得到的子序列(1)的长度为0,由此你可以想像得到,快速排序不适于对原本有序或基本有序的记录序列进行排序。 |
|
为避免出现枢轴记录关键字为"最大"或"最小"的情况,通常进行的快速排序采用"三者取中"的改进方案,即以
R[s]、R[t] 和 R[(s+t)/2] 三者中关键字介于中值者为枢轴。只要将它和 R[s] 互换,一次划分的算法仍不变。
关于快速排序时间复杂度的详细证明请读者查看教材所述。
|
|