目录
💡基本思想
💡基本框架
// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int* array, int left, int right)
{
if(right - left <= 1)
return;
// 按照基准值对array数组的 [left, right)区间中的元素进行划分
int div = partion(array, left, right);
// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
// 递归排[left, div)
QuickSort(array, left, div);
// 递归排[div+1, right)
QuickSort(array, div+1, right);
}
这是快速排序递归实现的主框架,可以发现与二叉树的递归十分相似,在递归时可以想想二叉树的递归规则。
💡分割方法
⭐Hoare版本
这是Hoare于1962年提出的一种二叉树结构的交换排序方法
这里其实我们保存的时基准值的下标,记为keyi,这样做是为了方便交换,不然交换时只是与key这个临时变量发生了交换而没有影响到原来的数组里的数。
⭐挖坑法
这个方法相较于hoare的方法更加好理解,但是性能上并没有太大的变化。
//挖坑法
int PartSort(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
swap(&a[begin], &a[midi]);
int key = a[begin];
int hole = begin;
while (begin < end)
{
//右边找小,填到左边的坑
while (begin < end && a[end] >= key)
{
end--;
}
a[hole] = a[end];
hole = end;
//左边找大,填到右边的坑
while (begin < end && a[begin] <= key)
{
begin++;
}
a[hole] = a[begin];
hole = begin;
}
a[hole] = key;
return hole;
}
⭐前后指针法
int PartSort(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
swap(&a[begin], &a[midi]);
int keyi = begin;
int prev = begin;
int cur = begin + 1;
while (cur <= end)
{
if (a[cur] < a[keyi] && ++prev != cur)//自身交换减少了
{
swap(&a[prev], &a[cur]);
}
cur++;
}
swap(&a[keyi], &a[prev]);
keyi = prev;
return prev;
}
💡优化方法
⭐三数取中法
所谓三数取中法,其实取的是三个数中的中位数,将这个数作为基准值,能够避免某些极端情况的出现(比如数组已经接近有序)。
⚠注:这是针对基数选取进行的优化,另外还有随机数法选数,在这里就不过多介绍了。
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
//取中位数
if (a[begin] <= a[midi])
{
if (a[midi] <= a[end])
{
return midi;
}
else
{
if (a[begin] <= a[end])
return end;
else
return begin;
}
}
else //midi begin
{
if (a[begin] >= a[end])
{
if (a[midi] >= end)
{
return midi;
}
else
return end;
}
else
return begin;
}
}
⭐小区间内使用插入排序
在递归到较小区间时,如果仍然使用快速排序,会造成时间上的浪费,假如这个区间内有7个数,那就要递归7次才能得到这个7个数的有序序列。
if(end-begin+1 <= 10)
{//某个区间内的小规模排序直接插入排序
//进行插入排序
InsertSort(arr,end-begin+1);
return;
}
💡非递归实现快速排序
void QuickSortNonR(int* a, int begin, int end)
{
ST s;
STInit(&s);
STPush(&s, end);
STPush(&s, begin);
while (!STEmpty(&s))
{
int left = STTop(&s);
STPop(&s);
int right = STTop(&s);
STPop(&s);
int keyi = PartSort(a, left, right);
// [left, keyi-1] keyi [keyi+1, right]
if (left < keyi - 1)
{
STPush(&s, keyi - 1);
STPush(&s, left);
}
if (keyi + 1 < right)
{
STPush(&s, right);
STPush(&s, keyi + 1);
}
}
STDestroy(&s);
}
💡性能分析
- 时间复杂度:最差O(N^2),最好O(NlogN),平均O(NlogN)
- 空间复杂度:O(logN),因为递归时创建的栈帧(申请的空间)没有销毁,递归的深度为logN
- 稳定性:不稳定
- 特点:数据越乱排序越快