引子:快速排序算法,被列为 20 世纪 十大算法之一。
希尔排序相当于直接插入排序的升级,只是增加了increment的设置,利用了基本有序的思想,同属于插入排序类。堆排序相当于简单选择排序的升级,他们同属于选择排序类,只是通过构建堆(完全二叉树)的方式,让选择最大项更加快速。而快速排序其实就是我们前面认为最慢的冒泡排序的升级,他们同属于交换排序类。即它也是通过不断比较和移动交换来实现排序的,不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减小了比较次数和移动交换次数。
基本思想:(Quick Sort )通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
代码:
/* 对顺序表 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);
}
}
/* 交换顺序表 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往左找,找到一个比 pivotkey小的元素 */
high--;
swap(L, low, high ); /* 右小 左大 互换 */
while(low < high && L->r[low] <= pivotkey ) /* 从low往右找,找到一个比pivotkey大的元素 */
low++;
swap(L, low, high); /* 左大 右小 互换 */
}
/* 最后结束循环,low 与 high 相等,返回low,返回位置 */
return low;
复杂度
时间复杂度 o(n log n)
空间复杂度 o(log n)
优化:
1. 优化选取枢纽(三数取中、九数取中),上述代码中,枢纽选择都是第一个元素,如果是最大或者最小元素,则会成为单只树。
2. 优化不必要的交换(将 pivotkey 被分到 L.r[0]中,然后是swap时,只做替换的工作,最终当 low 与 high 会和,即找到了枢轴的位置时,再将L.r[0] 的数值 赋值回 L.r[low]),因此减少了多次交换数据的操作。
3. 优化小数组时的排序方案(小数组时候,选用直接插入排序,快排对小数组排序没有优势,可能还要慢于最简单的插入排序)
4. 优化递归操作(对QSort实施尾递归优化,减小递归深度,提高效率)