超过最大递归深度堆栈溢出异常

问题描述

我目前正在编写一个算法来分析排序算法。我有很多输入,从 1000 个数字到 1 000 000 个输入。 目前我在使用快速排序功能时遇到了一些问题。由于我输入了 1 000 000 个类似数字(1-10 之间的数字),因此此代码将向我抛出错误 (0xC00000FD)(似乎是堆栈溢出异常)。现在,我不知道该怎么做才能减少递归调用数量,也不知道如何增加堆栈以便可能有多个递归调用。我附上了快速排序的代码

void swap(int *xp,int *yp)
    {
        int temp = *xp;
        *xp = *yp;
        *yp = temp;
    }

int partition (int arr[],int low,int high)
{
    int pivot = arr[(low+high)/2];
    int i = (low - 1);

    for (int j = low; j <= high - 1; j++)
    {
        if (arr[j] < pivot)
        {
            i++;
            swap(&arr[i],&arr[j]);
        }
    }
    swap(&arr[i + 1],&arr[high]);
    return (i + 1);
}
void quicksort(int A[],int l,int h)
{
    if (l < h) {
        int p = partition(A,l,h);
        quicksort(A,p - 1);
        quicksort(A,p + 1,h);
    }
}

解决方法

如果在递归过程中出现堆栈溢出,则意味着您的递归被破坏了。通常应该避免递归,因为它具有创建缓慢和危险算法的巨大潜力。如果您是一名初级程序员,那么我强烈建议您忘记您曾经听说过递归并停止阅读这里。

唯一可以合理允许的时间是递归调用放在函数的末尾,即所谓的“尾调用递归”。这几乎是编译器可以实际优化并用内联循环替换的唯一递归形式。

如果它不能进行尾调用优化,那么就意味着每次进行递归时实际上都调用了一个函数。这意味着堆栈不断堆积,您还会获得函数调用开销。这既是不必要的缓慢,又是不可接受的危险。因此,您编写的所有递归函数都必须针对目标进行反汇编,以确保代码没有出问题。

由于此代码似乎取自此站点 https://www.geeksforgeeks.org/iterative-quick-sort/,因此他们已经在那里为您描述了代码中的大部分问题。他们在底部有一个“quickSortIterative”函数,这是一个更好的实现。

我认为本教程的目的是向您展示一些损坏的代码(您问题中的代码),然后通过摆脱递归来演示如何正确编写它。

,

仅在较小的分区上递归就可以避免堆栈溢出:

void quicksort(int A[],int l,int h)
{
    while (l < h) {
        int p = partition(A,l,h);
        if((p - l) <= (h - p)){
            quicksort(A,p - 1);
            l = p + 1;
        } else {
            quicksort(A,p + 1,h);
            h = p - 1;
        }
    }
}

然而,最坏情况的时间复杂度保持在 O(n^2),问题代码中使用的 Lomuto 分区方案存在大量重复值的问题。 Hoare 分区方案没有这个问题(实际上更多的重复会导致更少的时间)。

https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme

快速排序中带有分区逻辑的示例代码:

void quicksort(int a[],int lo,int hi)
{
    int p;
    int i,j;
    while (lo < hi){
        p = a[lo + (hi - lo) / 2];
        i = lo - 1;
        j = hi + 1;
        while (1){
            while (a[++i] < p);
            while (a[--j] > p);
            if (i >= j)
                break;
            swap(a+i,a+j);
        }
        if(j - lo < hi - j){
            quicksort(a,lo,j);
            lo = j+1;
        } else {
            quicksort(a,j+1,hi);
            hi = j;
        }
    }
}