前言
在[C/C++]排序算法 快速排序 (递归与非递归)一文中,对于快速排序的单趟排序一共讲了三种方法: hoare、挖坑法、双指针法 ,这三种方法实现的快速排序虽然在一般情况下效率很高,但是如果待排序数据存在大量重复数据,那这几种方法的效率就很低,而为了解决快速排序在这样特殊情况下效率低下的问题, 三路划分就可以完美解决
三路划分
思想:
对于上述三种方法,其本质都是选定数组开头元素作特定值,让小的数据放左边,大的数据放右边。而三路划分顾名思义就是通过处理将数据分为三个部分 [小于特定值的部分 等于特定值的部分 大于特定值的部分] ,这样划分好后,只需要对小于特定值的部分和大于特定值的部分进行递归排序即可,中间的数据就不需要处理了,相比于上述三种方法效率提升很大,并且重复数据越多排序效率越快,当带排序数据全为重复数据时,时间复杂度甚至可以达到O(N)。
算法实现
[解释]:
为什么a[begin]和a[cur]交换后, cur要++, 而a[end]和a[cur]交换后,cur不和情况1一样++呢?
因为单趟排序排好后划分了三个部分,我们处理两边的部分需要返回两个值,所以就不单独封装三路划分的单趟排序了
代码如下:
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[mid])
{
if (a[mid] > a[end])
return mid;
else if (a[begin] > a[end])
return end;
else
return begin;
}
else
{
if (a[begin] > a[end])
return begin;
else if (a[mid] > a[end])
return end;
else
return mid;
}
}
void QuickSort(int* a, int begin, int end)
{
if (begin >= end)
return;
int mid = GetMid(a, begin, end);
swap(&a[begin], &a[mid]);
//由于begin和end要改变,提前保存,便于递归使用
int left = begin;
int right = end;
int cur = begin + 1;
int key = a[begin];
while (cur <= end)
{
if (a[cur] < key)
{
swap(&a[cur], &a[begin]);
begin++;
cur++;
}
else if (a[cur] > key)
{
swap(&a[cur], &a[end]);
end--;
}
else
{
cur++;
}
}
QuickSort(a, left, begin - 1);
QuickSort(a, end + 1, right);
}