快排时间复杂度:O(NlogN) ,快速排序的空间复杂度为O (log n),其中n为待排序数组的长度。 这是因为快速排序是一种原地排序算法,它通过在原始数组上进行交换和划分操作来实现排序,而不需要额外的空间来存储临时数据。 在每一次递归调用中,快速排序只需要使用O (log n)的额外空间来保存递归调用的栈空间
挖坑法是基于Hoare版本的一种改良(改良单趟排序时的方法),其本质是类似的。先来个图自己感受一下。
将基准值设为“坑”,然后右边先走,找小,找到小的后,将“小”填入坑,再将原来小的位置,重置为坑,接着左边走。直到left和right相遇时,一定相遇为“坑”(因为left和right始终有一个为坑,那么坑左边为小,右边为大,再将key放入坑中),循环结束。
同样,我们采取“三数取中”法来进行优化:
//这样可以有效的提高效率
//避免每次单趟排序时的begin元素都是最大或最小
int GetMidi(int* arr, int begin, int end)
{
int midi = (begin + end) / 2;
if (arr[begin] < arr[end])
{
if (arr[midi] >= arr[begin] && arr[midi] <= arr[end])
return midi;
if (arr[midi] >= arr[end])
return end;
if (arr[midi] <= arr[begin])
return begin;
}
else
{
if (arr[midi] >= arr[end] && arr[midi] <= arr[begin])
return midi;
if (arr[midi] >= arr[begin])
return begin;
if (arr[midi] <= arr[end])
return end;
}
}
然后进行单趟排序:将基准值排到中间,最后返回此时基准值的下标。
//单趟
int SingleQuick_DigHole(int* arr, int begin, int end)
{
int key = arr[begin]; //取定基准值
int holei = begin; //定义“坑”的下标
int left = begin, right = end;
while (left < right)
{
//同理右边先走,右找小
while (left < right && arr[right] >= key)
{
right--;
}
//找到小后,将这个小的值挪到“坑”里面去,然后right变成新的坑
arr[holei] = arr[right];
holei = right;
//左找大
while (left < right && arr[left] <= key)
{
left++;
}
//找到大后,将这个大的值挪到“坑”里面去,然后left变成新的坑
arr[holei] = arr[left];
holei = left;
}
//最后当left和right相遇时,必定是在坑处相遇,将基准值挪到“坑”里面,然后返回坑的下标
arr[holei] = key;
return holei;
}
最后递归[begin,keyi-1]和[keyi+1,end]:
void QuickSort_DigHole_incline(int* arr, int begin, int end)
{
if (begin >= end)
return;
int midi = GetMidi(arr, begin, end);
Swap(&arr[begin], &arr[midi]);
int keyi = SingleQuick_DigHole(arr, begin, end);
//继续递归坑的左区间和右区间
//[begin,keyi-1] , [keyi+1,end]
QuickSort_DigHole_incline(arr, begin, keyi - 1);
QuickSort_DigHole_incline(arr, keyi + 1, end);
}
“挖坑法”和Hoare的办法从本质上来说是一致的,差别就是在单趟排序的方法不同,理解好了Hoare版本的单趟排序,那么对“挖坑法”理解难度就不大了。