双路快速排序的时间复杂度为O(nlog2n),空间复杂度为O(n)
双路快速排序的核心思想:单路快排会将等于V的元素分配在左侧或者右侧,当数组中有大量重复元素时,这将会导致左右两侧的元素数量极度不均衡,时间复杂度退化到O(n^2),如下图所示:
双路快排是将等于V的部分近乎平均的分配在左右两侧,避免了该问题
说明:双路快速排序在等于V的时候也要交换位置,是为了避免大量等于V的元素集中在左侧或者右侧
排序的步骤和示意图如下:
- i从l+1的位置开始递增,直到第一个大于等于V的元素
- j从r的位置开始递减,直到第一个小于等于V的元素
- 两块蓝绿色区域分别归到左侧和右侧,交换i和j指向的元素,并且i ++,j--,可以看出等于V的元素近乎均分在了左右两侧
- 继续次操作,直到i >j,注意此时j指向的是最后一个小于等于V的元素,i指向的是第一个大于等于V的元素,所以交换j与L位置的元素,本层双路快排结束
- 重复上述步骤,直到所有元素有序
代码如下:
template<typename T>
void quickSort2Ways(T arr[], int n) {
srand(time(nullptr));
_quickSort2Ways(arr, 0, n - 1);
}// 对arr[rangeL,rangeR]部分进行快速排序
template<typename T>
void _quickSort2Ways(T arr[], int rangeL, int rangeR) {
if (rangeL >= rangeR) {
return;
}if (rangeR - rangeL <= 16) {
insertionSort(arr, rangeL, rangeR);
return;
}int index = _quickPartition2Ways(arr, rangeL, rangeR);
_quickSort2Ways(arr, rangeL, index - 1);
_quickSort2Ways(arr, index + 1, rangeR);
}// 双路快速排序的partition
// 返回p, 使得arr[rangeL,index] <= arr[index] ; arr[index+1,rangeR] >= arr[index]
// 双路快排处理的元素正好等于arr[index]的时候要注意,详见下面的注释:)
template<typename T>
int _quickPartition2Ways(T arr[], int rangeL, int rangeR) {
// 随机在arr[rangeR, rangeL]的范围中, 选择一个数值作为标定点pivot
swap(arr[rangeL], arr[rand() % (rangeR - rangeL + 1) + rangeL]);T temp = arr[rangeL];
// arr[rangeL+1,i) <= temp; arr(j,rangeR] >= temp
int i = rangeL + 1, j = rangeR;
while (true) {
// 注意这里的边界, arr[i] < temp, 不能是arr[i] <= temp
while (i <= rangeR && arr[i] < temp)
i++;// 注意这里的边界, arr[j] > temp, 不能是arr[j] >= temp
while (j >= rangeL + 1 && arr[j] > temp)
j--;if (i > j)
break;swap(arr[i], arr[j]);
i++;
j--;
}
swap(arr[j], arr[rangeL]);
return j;
}