我开始研究Yaroslavskiys Dual Pivot Quicksort的修改版本,作为第一步,我尝试删除递归(代码基于此paper中的递归版本)。这似乎有点老式,但我仍然想尝试一下。这是我到目前为止的C代码。它按预期工作,但我有点担心本地堆栈可能溢出的病态情况:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define QS_STACK_SIZE 96
#define QS_ARR_SIZE 10000000
#define INT_SWAP(x_,y_)\
do {\
int tmp_ = (x_);\
(x_) = (y_);\
(y_) = tmp_;\
} while(0)
void qsort_yaro(int *a, int left, int right, int div);
int main(void)
{
int i;
static int a[QS_ARR_SIZE];
srand (time (NULL));
for (i = 0; i < QS_ARR_SIZE; i ++)
a[i] = rand();
printf ("Sorting %i elements ...\n", QS_ARR_SIZE);
qsort_yaro (a, 0, QS_ARR_SIZE - 1, 3);
for (i = 0; i < QS_ARR_SIZE - 1; i ++)
if (a[i] > a[i + 1])
{
printf ("ERROR : Sequence not ascending!\n");
break;
}
if (i == QS_ARR_SIZE - 1)
printf ("OK : Sequence ascending!\n");
return 0;
}
void qsort_yaro(int *a, int left, int right, int div)
{
int i, j, k, third;
int len;
int m1, m2;
int pivot1, pivot2;
int less, great;
int dist;
int hi[QS_STACK_SIZE];
int lo[QS_STACK_SIZE];
int lvl = -1;
int max_stack = 0;
lo[++lvl] = left;
hi[lvl] = right;
while (lvl >= 0)
{
left = lo[lvl];
right = hi[lvl--];
while ((len = right - left) >= 27)
{ // insertion sort for tiny array
third = len / div;
//
// "medians"
m1 = left + third;
m2 = right - third;
if (m1 <= left)
m1 = left + 1;
if (m2 >= right)
m2 = right - 1;
if (a[m1] < a[m2])
{
INT_SWAP(a[m1], a[left]);
INT_SWAP(a[m2], a[right]);
}
else
{
INT_SWAP(a[m1], a[right]);
INT_SWAP(a[m2], a[left]);
}
// pivots
pivot1 = a[left];
pivot2 = a[right];
// pointers
less = left + 1;
great = right - 1;
// sorting
for (k = less; k <= great; k++)
{
if (a[k] < pivot1)
{
INT_SWAP(a[k], a[less]);
less++;
}
else if (a[k] > pivot2)
{
while (k < great && a[great] > pivot2)
great--;
INT_SWAP(a[k], a[great]);
great--;
if (a[k] < pivot1)
{
INT_SWAP(a[k], a[less]);
less++;
}
}
}
// swaps
dist = great - less;
if (dist < 13)
div++;
INT_SWAP(a[less - 1], a[left]);
INT_SWAP(a[great + 1], a[right]);
// lo[++lvl] = left;
// hi[lvl] = less - 2;
// lo[++lvl] = great + 2;
// hi[lvl] = right;
// Push the smaller interval on top for less stack usage
// subarrays
if (less - 2 - left > right - great - 2)
{
lo[++lvl] = left;
hi[lvl] = less - 2;
lo[++lvl] = great + 2;
hi[lvl] = right;
}
else
{
lo[++lvl] = great + 2;
hi[lvl] = right;
lo[++lvl] = left;
hi[lvl] = less - 2;
}
if (max_stack < lvl)
max_stack = lvl;
// equal elements
if (dist > len - 13 && pivot1 != pivot2)
{
for (k = less; k <= great; k++)
{
if (a[k] == pivot1)
{
INT_SWAP(a[k], a[less]);
less++;
}
else if (a[k] == pivot2)
{
INT_SWAP(a[k], a[great]);
great--;
if (a[k] == pivot1)
{
INT_SWAP(a[k], a[less]);
less++;
}
}
}
}
// subarray
if (pivot1 < pivot2)
{
left = less;
right = great;
}
else
{
left = lo[lvl];
right = hi[lvl--];
}
}
for (i = left + 1; i <= right; i++)
for (j = i; j > left && a[j] < a[j - 1]; j--)
INT_SWAP (a[j], a[j - 1]);
}
printf ("Max Stack Size : %i\n", max_stack);
}
我通过这个小小的改变调整了最初的实施
if (less - 2 - left > right - great - 2)
{
lo[++lvl] = left;
hi[lvl] = less - 2;
lo[++lvl] = great + 2;
hi[lvl] = right;
}
else
{
lo[++lvl] = great + 2;
hi[lvl] = right;
lo[++lvl] = left;
hi[lvl] = less - 2;
}
基于与传统迭代1-枢轴实现中相同的思想,其中较大的部分被推入堆栈以便将堆栈大小限制为log(n)。在这里,较小的部分放在堆栈的顶部,以便尽早将其弹回。
我使用随机生成的10.000.000元素数组进行了一些实验,实际上可以通过此方法保存多达5个堆栈级别(最多约45个)。
现在,我没有找到一个严格的论据,为什么在这个实现中堆栈大小应该被常量限制,所以要么我在这里监督数学,要么我需要一个更好的想法和实现。