为什么我的快速排序有时会出现堆栈溢出?

时间:2018-04-23 14:07:43

标签: java

当我向此方法发送一个大小为>的数组时900000,大约一半时间我得StackOverflowError。请帮助我理解为什么会发生这种情况以及如何解决它。

public static void quicksort2(int[] xs, int lo, int hi){
    int h,l,p,t;
    if (lo<hi){
        l = lo;
        h = hi;
        p = xs[hi];

        do {
            while( (l<h) && (xs[l] <= p) )
                l= l+1;

            while( (h > l) && (xs[h] >= p))
                h = h-1;

            if (l < h){
                t = xs[l];
                xs[l] = xs[h];
                xs[h] = t;
            }
        } while (l<h);
        xs[hi] = xs[l];
        xs[l] = p;

        quicksort2(xs, lo, l-1);
        quicksort2(xs, l+1,hi);

    }
}

我得到的具体错误是:

Exception in thread "main" java.lang.StackOverflowError
at sort.Sorters.quicksort2(Sorters.java:117)
at sort.Sorters.quicksort2(Sorters.java:117)
at sort.Sorters.quicksort2(Sorters.java:118)
at sort.Sorters.quicksort2(Sorters.java:117)
at sort.Sorters.quicksort2(Sorters.java:117)

然后还有更多同样的事情。引用的两行是递归调用。

1 个答案:

答案 0 :(得分:0)

您实施快速排序很好。通过使用whilel++而不是h--l=l+1并在h=h-1语句中使用大括号来更好地使用变量名称可以稍微改善一下其他一些小细节。

问题在于你这样做:

int[] willBlowUp = new int[1000000];
quicksort2(willBlowUp, 0, willBlowUp.size - 1);

这将生成StackOverflowError,因为它是一个退化的情况,使调用堆栈与输入一样高。而且我不知道任何JVM的堆栈部分足够大,可以进行一百万次递归调用。

给定的数组仅包含一百万个零。因此,在排序之前按相反的顺序排序或进行排序是没有希望的。 while循环中的第一个do将始终移动l直到分区的最后,留下第二部分用于递归调用,只有一个单独的元素,第一部分包含所有内容其他。这意味着在每个递归级别,数组的未排序区域仅减少1个项目,理想情况下它应减少一半。

此外,由于while内的第一个do循环必须执行总计半万亿次迭代(如果它不是StackOverflowError,性能非常糟糕)。

如果你使用胖快速排序和改组,这个特殊情况可以解决,但是总有一种情况,即使在统计上不太可能再次发生这个问题。让我们假设(即使在洗牌后)你得到一个由1,2,3,4,5,...,n组成的数组。 Quicksort在这种情况下会表现得很糟糕。

有一些解决方案,但它们都很复杂和混乱,比如混合算法,在阵列的某些子集上有选择地切换到另一个,在选择枢轴元素之前分析数据的结构或如果shuffle的输出是坏的,那就重新洗牌。