【问题标题】:QuickSort stack overflow on big input大输入时快速排序堆栈溢出
【发布时间】:2015-08-05 00:57:22
【问题描述】:

如果我提供反向排序的数组,我的 QuickSort 实现会导致 StackOverflow 错误。它适用于大约 1000 个项目,但对于 10000+ 个项目,我收到 StackOverflow 错误。如果出现错误,则递归深度约为 9000。我知道我的算法总是选择子数组的最新元素作为枢轴,这不是最优的,但我不会改变它,因为我想让它像这样工作。 代码如下:

private int partition(int[] numbers, int begin, int end) {
    int pivot = numbers[end];
    int partitionIndex = begin;
    for (int i = begin; i < end; ++i) {
        comparisonCounter++;
        if (numbers[i] <= pivot) {
            if (i != partitionIndex) {
                swapCounter++;
                int temp = numbers[i];
                numbers[i] = numbers[partitionIndex];
                numbers[partitionIndex] = temp;
            }
            partitionIndex++;
        }
    }
    if (partitionIndex != end) {
        swapCounter++;
        int temp = numbers[partitionIndex];
        numbers[partitionIndex] = numbers[end];
        numbers[end] = temp;
    }
    return partitionIndex;
}

private void quickSort(int[] numbers, int begin, int end) {
    if (begin < end) {
        int partitionIndex = partition(numbers, begin, end);
        quickSort(numbers, begin, partitionIndex - 1);
        quickSort(numbers, partitionIndex + 1, end);
    }
}

我的实现有问题吗?我该如何解决? 谢谢!

【问题讨论】:

  • 如果列表是一个反向排序的数组,并且您总是选择最后一个元素作为枢轴,似乎预计您将获得大致长度的递归深度列表,因为您的分区之一将是空的。
  • 来自Wikipedia,“分区的最左边的元素通常会被选为轴心......这会导致已经排序的数组出现最坏情况。”由于您使用最后一个作为枢轴和反向数组,因此这是最坏的情况。所以你真的没有一个好的实现,因为你将有 O(n^2)。您应该使用中间元素或其他元素作为枢轴。
  • 谢谢你们俩。是的,我测试了最坏的情况,然后我得到了错误。现在我可以将其重写为迭代方法。

标签: java stack-overflow quicksort


【解决方案1】:

您的代码似乎没有任何问题,但请记住,这是一个递归函数,并且大多数语言都有一个有限深度的堆栈,如果您有足够大的输入,您一定会得到这个堆栈。对于 Java,请参阅:

您可以做的是将您的方法从递归方法转变为迭代方法。有几种方法可以做到这一点。我刚刚在网上找到了几个例子:

【讨论】:

  • 非常感谢您的快速答复!如您所见,我的实施还可以吗?我只需要处理java堆栈或将其重写为迭代方法?
  • @marfoldi 没有在您的代码中详细介绍,但快速浏览一下它看起来不错。同样,您报告的错误是所有递归调用中的常见错误。如果您只想将其排除在外,请尝试调整分配给堆栈的内存。如果您想要一个更长期的解决方案,或者您想练习您的编码,请继续尝试使您的解决方案具有迭代性。不过,对于任何通用排序,内置的 Java 排序方法都提供了最合理和最稳定的优化。
【解决方案2】:

有一些方法可以减少快速排序中的堆栈大小。

  • 您可以每次都将较大的分区放在堆栈上,将较小的分区先排序,然后将其留在堆栈中。这样可以防止太多的小分区一次阻塞堆栈。

  • 您可以使用另一种方法(例如插入排序)对小分区进行排序,而不是使用快速排序。同样,这使许多小分区远离堆栈。您可以进行试验,但 15 到 20 个元素的区域通常算作“小”分区。

正如您所说,使用更好的方法来选择您的支点也会有所帮助。

【讨论】:

    猜你喜欢
    • 2015-06-06
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 2016-02-21
    • 2015-07-12
    相关资源
    最近更新 更多