双路快速排序的时间复杂度为O(nlog2n),空间复杂度为O(n)
双路快速排序的核心思想:单路快排会将等于V的元素分配在左侧或者右侧,当数组中有大量重复元素时,这将会导致左右两侧的元素数量极度不均衡,时间复杂度退化到O(n^2),,如下图所示:为了避免该问题,双路快排是将等于V的部分近乎平均的分配在左右两侧;三路快排是将等于V的部分集中到中间,左右两侧分别为小于V和大于V的部分,这样不用对大量等于V的元素重复操作,可以一次性把大量重复元素排好序
说明:三路快排只对有大量重复数据的排序效率比较高,普通数据或者基本有序的数据效率虽然不如双路快排和基础快排,总体来说三路快排的性能是比较有保证的,一般情况下系统排序都会选择三路快排,运行效率如下图:
排序的步骤和示意图如下:
- lt指向L的位置,i指向L+1的位置,gt指向r+1的位置
- 如果i指向的元素等于V,则i++考察下一个元素
- 如果i指向的元素小于V,则交换lt+1与i指向的元素,,然后lt++,并且i++考察下一个元素
- 如果i指向的元素大于V,则交换gt-1与i指向的元素,然后gt--,注意此时i无需自增,因为i指向的是未被处理的元素
- 循环上述步骤,直到i==gt,循环结束
- 交换L与lt指向的元素,注意交换后lt指向的元素等于V
- 逐层递归,直到所有的元素有序,注意下次迭代秩序排序[l, lt-1]和[gt, r]两部分,等于V的部分无需再次排序
代码如下:
template<typename T>
void quickSort3Ways(T arr[],int n) {
_quickSort3Ways(arr, 0, n - 1);
}// 对arr[rangeL,rangeR]部分进行快速排序
template<typename T>
void _quickSort3Ways(T arr[], int rangeL, int rangeR) {
if (rangeL >= rangeR) {
return;
}
if (rangeR - rangeL <= 16) {
insertionSort(arr, rangeL, rangeR);
return;
}// 随机在arr[rangeR, rangeL]的范围中, 选择一个数值作为标定点pivot
swap(arr[rangeL], arr[rand() % (rangeR - rangeL + 1) + rangeL]);T temp = arr[rangeL];
int lt = rangeL; // arr[rangeL+1,lt] < temp
int gt = rangeR + 1; // arr[gt...rangeR] > temp
int i = rangeL + 1; // arr[lt+1...i) == temp
while (i < gt) {
if (arr[i] < temp) {
swap(arr[i], arr[lt + 1]);
i++;
lt++;
}
else if (arr[i] > temp) {
swap(arr[i], arr[gt - 1]);
gt--;
}
else { // arr[i] == temp
i++;
}
}//交换之后arr[lt] = temp
swap(arr[rangeL], arr[lt]);_quickSort3Ways(arr, rangeL, lt - 1);
_quickSort3Ways(arr, gt, rangeR);
}