Quicksort给出了常量数字的stackoverflow,但不是随机数

时间:2016-02-29 09:39:28

标签: c++ arrays algorithm sorting quicksort

当我使用rand()或任何常量值分配数组值时,我对此C ++代码段的行为有何不同感到非常困惑。

const int MIN_SIZE = 10000;
const int MAX_SIZE = 100000;

int main()
{
  for(j = MIN_SIZE; j <= MAX_SIZE; j += MIN_SIZE) {
    int *arrPtr = new int[j];
    for(int i = 0; i < j; i++)
       arrPtr[i] = 1; //When I put rand() here, it works fine but in any constant it gives stack overflow
    quickSort(arr, 0, j - 1);
    delete []arrPtr;
  }
}

上面的代码基本上创建了一个动态分配的数组,其大小为j,每回合增加MIN_SIZE(10,000)并为每个索引分配一些特定的整数。在赋值之后,它将使用我将在下面提供的快速排序算法进行排序,然后在完成后释放该数组。整件事重复到MAX_SIZE(100,000)。

这是我的Quicksort代码:

void quickSort(int *arr, int front, int rear)
{
    if (front < rear)
    {
        int part = partition(arr, front, rear);
        quickSort(arr, front, part - 1);
        quickSort(arr, part + 1, rear);
    }
}

int partition(int *arr, int front, int rear)
{
    int element = arr[rear];
    int i = front - 1;
    for (int j = front; j<rear; ++j)
    {
        if (arr[j] <= element)
        {
            ++i;
            long temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    long temp = arr[i + 1];
    arr[i + 1] = arr[rear];
    arr[rear] = temp;
    return i + 1;
}

我正在尝试实施快速排序算法,该算法严格使用 最后一项作为支点 。在这种情况下,我面临一个奇怪的问题:当我使用rand()函数将数组的每个值分配给一个随机数时,一切正常,但是,当我输入一个常量值时,大小数组上升到4039(当你操作MAX_SIZE和MIN_SIZE时)然后给出堆栈溢出错误。我真的很困惑,为什么在地球上会导致问题,另外,为什么4039?

3 个答案:

答案 0 :(得分:6)

使用最后一个元素作为枢轴元素的快速排序,当以直接的方式实现时,预计会溢出堆栈以获得相同的元素。这就是quicksort的工作原理。这是算法中的“缺陷”。

了解为什么只看一下如何创建递归函数调用。

quicksort(arr, 0, 100)  - will produce the recursive calls
   quicksort(arr, 0, 99); and
   quicksort(arr, 100, 100);

问题是quicksort(arr, 0, 99);将递归数组中的每个元素。

在您的情况下,您的堆栈已满4039元素。在每次调用中,您似乎都有大约8个整数状态,这将为您提供堆栈最大大小的提示。我猜大约1 MB。

随机整数的情况并非如此,因为递归调用的深度将均匀地分布在递归的左侧和右侧部分之间。这种期望的行为使得递归深度方法记录为N.对于MAX_SIZE,这是大约17的深度,而不是100000.这就是将快速排序描述为N log N算法的原因。第一个N来自分区。

答案 1 :(得分:2)

具有end-pivot并将数组拆分为两个的常量数组导致递归深度为&#34;数组中的元素数&#34;和O(n ^ 2)时间。

有很多方法可以解决这个问题。

首先,将数组分为3个组件。分区更大,更少,等于。平等之间。这可以修复您遇到的角落案例。它增加了常数因子,但快速排序成本变为O(n lg m),其中m是作为奖励的不同元素的数量。

排序阵列仍然死于可怕的死亡。做一个更好的partiton选择器。随机分区使得可怕行为的概率接近0.挑选3(或2k + 1)个元素(可能是随机的)并使用它们的中位数是另一种方法。对于确定性良好行为,在O(n)时间内找到30%和70%标记之间的元素的算法被称为&#34;中值为5&#34; (这不仅仅取5个元素的中位数。)

另一个技巧是对数组进行分区,在较小的分区上进行递归,在较大的分区上进行循环。这解决了递归深度问题,但没有解决运行时问题。

接下来,考虑小数组长度的转义策略。与选择排序相比,对(例如)8个元素进行快速排序可能是非常不理想的。一旦你有一个逃生策略,你可以优化使用快速和脏的快速排序(为枢轴选择3个随机元素等)并跟踪递归深度。如果你传递2 * lg(n)深度,则逃脱以证明正确的快速排序(中位数为5以找到枢轴)。当你跌到少于8(调整这个)元素时,切换到选择排序。

最后,当您std::sort时,所有上述内容和更多内容可能已经完成。所以请改用它。

答案 2 :(得分:0)

如果您总是首先递归到较小的一半而编译器为第二次调用生成尾递归,则可以保证堆栈深度为O(log(N))

void quickSort(int *arr, int front, int rear)
{
    if (front < rear)
    {
        int part = partition(arr, front, rear);
        int a, b, c, d;
        if (part - front <= rear - part)
        {
            a = front;
            b = part - 1;
            c = part + 1;
            d = rear;
        }
        else
        {
            a = part + 1;
            b = rear;
            c = front;
            d = part - 1;
        }
        quickSort(arr, a, b);
        quickSort(arr, c, d);
    }
}