快速排序基本思路:找到一个标定点,左边的元素小于标定点,右边元素大于标定点,然后再对左右区间递归快速排序。
// 对arr[l...r]部分进行partition操作
// 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
template <typename T>
int __partition(T arr[], int l, int r) {
T v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for (int i = l + 1; i <= r; i++)
if (arr[i] < v) {
j++;
swap(arr[j], arr[i]);
}
swap(arr[l], arr[j]);
return j;
}
// 对arr[l...r]部分进行快速排序
template <typename T>
void __quickSort(T arr[], int l, int r) {
if (l >= r)
return;
int p = __partition(arr, l, r);
__quickSort(arr, l, p - 1);
__quickSort(arr, p + 1, r);
}
template <typename T>
void quickSort(T arr[], int n) {
__quickSort(arr, 0, n - 1);
}
改进1:如果数组近乎有序,标定点的选择会影响快速排序的性能,如果每次都选择最小值作为标定点,快速排序时间复杂度会退化为,因此需要随机化标定点元素。
template <typename T>
int __partition(T arr[], int l, int r) {
// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap(arr[l], arr[rand() % (r - l + 1) + l]);
T v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for (int i = l + 1; i <= r; i++)
if (arr[i] < v) {
j++;
swap(arr[j], arr[i]);
}
swap(arr[l], arr[j]);
return j;
}
改进2:如果数组有大量相同元素,上面的做法会将相同元素分到大于v的区间,会造成两个子区间元素不平衡,所以需要将相等元素平衡地分入两个区间。
template <typename T>
int _partition2(T arr[], int l, int r) {
// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap(arr[l], arr[rand() % (r - l + 1) + l]);
T v = arr[l];
//[l+1,i) <=v, (j,r]>=v
int i = l + 1, j = r;
while (i <j)
{
while (i <=r&&arr[i] < v)
i++;
while (j >l && arr[j-1] > v)
j--;
swap(arr[i], arr[j]);
i++; j--;
}
swap(arr[i], arr[l]);
return i;
}
对应地,还有三路快速排序算法。