前言
当数组中有大量重复的元素时用三划分可以有效地提升效率,并且当数组中没有大量重复元素时该算法的效率也不低于原快速排序的效率
思路
将整个数组分成三份,也就是找两个分界点,我们假设分界点l,r满足l<r,则中间有r−l个等于v的元素,左右分别为小于v和大于v的元素将整个数组分成三份,也就是找两个分界点,我们假设分界点l,r满足l<r,则中间有r-l个等于v的元素,左右分别为小于v和大于v的元素将整个数组分成三份,也就是找两个分界点,我们假设分界点l,r满足l<r,则中间有r−l个等于v的元素,左右分别为小于v和大于v的元素。
根据这个逻辑,我们可以将等于v的元素分别放到pattern中数组的两端,最后把两端的数字转移到中央。因为根据快排的缘故,最终在中间的数字必然是左边小于v,右边大于v,只要各自把这两段交换一下即可。
为了快速写出正确高效的快排,之后pattern的方式均以下面的形式,这种形式优点是可以少写要交换时的两个指针的移动
实现
void QuickSort(Elem arr[], int l, int r) {
if(l >= r) return ;
int ltag, rtag, left, right;
Elem v = arr[r];
left = ltag = l - 1;
right = rtag = r;
while(1) {
while(arr[++left] < v);
while(arr[--right] > v) if(left == right) break;
if(left >= right) break;
swap(arr[left], arr[right]);
if(arr[left] == v) {++ltag; swap(arr[left], arr[ltag]); } //当其相等后被拉到旁边,而旁边那个数之前已经验证必然<=v所以之后判++left的元素显然不会产生bug,右边同理
if(arr[right] == v) {--rtag; swap(arr[right], arr[rtag]); }
}
swap(arr[left], arr[r]); //把标兵归为
right = left + 1; left = left - 1; //相当于把标兵v左右区间和它相同的拉到中间
for(int i = l; i < ltag; i++, left--) swap(arr[i], arr[left]);
for(int i = r - 1; i > rtag; i--, right++) swap(arr[i], arr[right]);
QuickSort(arr, l, left);
QuickSort(arr, right, r);
}
主要的pattern:
Elem v = arr[r];
left = ltag = l - 1;
right = rtag = r;
while(1) {
while(arr[++left] < v);
while(arr[--right] > v) if(left == right) break;
if(left >= right) break;
swap(arr[left], arr[right]);
}
swap(arr[left], arr[r]);
return left;