上一节讲了快排算法在序列基本有序的情况下的两种优化方法,这一节我们对新的测试用例进行测试,测试用例如下所示:
int main()
{
//测试 - 待排序列的重复值很多
int n = 400000;
int *arr = SortTestHelper::generateRandomArray(n, 0, 10);
int *arr2 = SortTestHelper::copyIntArray(arr, n);
SortTestHelper::testSort("mergeSort", mergeSort, arr, n);
SortTestHelper::testSort("qucikSort2", quickSort, arr2, n);
delete[] arr;
delete[] arr2;
return 0;
}
测试结果如下:
可见,对于数据量很大,并且重复数据很多的情况,即使是上一节优化过的快排算法,其时间性能也是不能接受的,这里针对这种重复数据量大的情况对快排算法作出进一步优化。
首先,先看__patition()函数的操作:
template<typename T>
int __patition(T arr[], int l, int r)
{
//在arr[l...r]中随机选取一个元素作为标定点,并将其和序列第一个元素交换
swap(arr[l], arr[rand() % (r - l + 1) + l]);
T v = arr[l];
int j = l;
for(int i = l + 1; i <= r; i++)
{
if(arr[i] < v)
{
swap(arr[j + 1], arr[i]);
j++;
}
}
swap(arr[l], arr[j]);
return j;
}
在 if 语句中,是将<v的元素放在橙色部分,而>=v的元素全部放置在紫色部分。
当序列中存在大量重复元素(即大量等于v的元素时),最后就会出现极度不平衡的现象,如下:
那么这样得到的快排递归树也是很不平衡的,树的深度就会超过logn,总的时间复杂度就会大于O(nlogn)很多,甚至退化成O(n^2)的时间复杂度。
于是,对快排算法我们采取另一种思想:即不是将<v和>v的部分都放在序列的一头,而是分别放在序列的左右两头,如下所示: