前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值
基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。
快速排序实现主框架:
//快速排序
void QuickSort(int* arr, int left, int right)
{
if (left >= right)
{
return;
}
//keyi即是基准值
int keyi = _QuickSort1(arr, left, right);//实现找基准值的方法
QuickSort(arr, left, keyi - 1);
QuickSort(arr, keyi + 1, right);
}
Swap方法的实现 ,即交换两个数的值
void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
hoare版本
算法思路:
1)创建左右指针,确定基准值
2)从右向左找出比基准值小的数据,从左向右找比基准值大的数据,左右指针数据交换,进入下次循环
问题1:为什么跳出循环后right位置的值⼀定不大于key?
当 left > right 时,即right⾛到left的左侧,而left扫描过的数据均不大于key,因此right此时指向的数据⼀定不大于key
问题2:为什么left 和 right指定的数据和key值相等时也要交换?
相等的值参与交换确实有⼀些额外消耗。实际还有各种复杂的场景,假设数组中的数据⼤量 重复时, 无法进行有效的分割排序。
int _QuickSort1(int* arr, int left, int right)
{
int keyi = left;
++left;
while (left <= right)
{
while (left <= right && arr[right] > arr[keyi])
{
right--;
}
while (left <= right && arr[left] < arr[keyi])
{
left++;
}
if (left <= right)
{
Swap(&arr[left++], &arr[right--]);
}
}
Swap(&arr[keyi], &arr[right]);
return right;
}
挖坑法
思路: 创建左右指针。首先从右向左找出比基准小的数据,找到后立即放入左边坑中,当前位置变为新的"坑",然后从左向右找出比基准大的数据,找到后立即放入右边坑中,当前位置变为新的"坑",结束循环后将最开始存储的分界值放入当前的"坑"中,返回当前"坑"下标(即分界值下标)
int _QuickSort2(int* arr, int left, int right)
{
int hole = left;//坑
int key = arr[hole];//坑位数据
while (left < right)
{
while (left < right && arr[left] >= key)
{
--right;
}
arr[hole] = arr[right];
hole = right;
while (left < right && arr[left] < key)
{
++left;
}
arr[hole] = arr[left];
hole = left;
}
arr[hole] = arr[left];
return hole;
}
lomuto前后指针
创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。
int _QuickSort3(int* arr, int left, int right)
{
int prev = left, cur = left + 1;
int keyi = left;
while (cur <= right)
{
if (arr[cur] < arr[keyi] && ++prev != cur)
{
Swap(&arr[cur], &arr[prev]);
}
++cur;
}
Swap(&arr[keyi], &arr[prev]);
return prev;
}
以上就是快速排序当中查找基准值的三种方法
快速排序特性总结:
1. 时间复杂度:O(nlogn)
2. 空间复杂度:O(logn)
快速排序非递归版本,借助数据结构栈
void QuickSortNonR(int* arr, int left, int right)
{
ST st;
STInit(&st);//栈的初始化
STPush(&st, right);//压栈
STPush(&st, left);//压栈
while (!STEmpty(&st))
{
//取栈顶元素两次
int begin = STTop(&st);
STPop(&st);
int end = STTop(&st);
STPop(&st);
//找基准值[begin,end]
int prev = begin;
int cur = begin + 1;
int keyi = begin;
while (cur <= end)
{
if (arr[cur] < arr[keyi] && ++prev != cur)
{
Swap(&arr[cur], &arr[prev]);
}
cur++;
}
Swap(&arr[keyi], &arr[prev]);
keyi = prev;
//根据基准值划分左右区间
//左区间:[begin,keyi-1]
//右区间:[keyi+1,end]
if (keyi + 1 < end)
{
STPush(&st, end);
STPush(&st,keyi + 1);
}
if (keyi - 1 > begin)
{
STPush(&st, keyi - 1);
STPush(&st, begin);
}
}
STDestroy(&st);//销毁
}