大输入时 QuickSort 堆栈溢出
QuickSort stack overflow on big input
如果我给出反向排序的数组,我的 QuickSort 实现会导致 Whosebug 错误。它适用于大约 1000 个项目,但对于 10000+ 个项目,我收到 Whosebug 错误。如果我得到错误,递归深度约为 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);
}
}
我的实现有问题吗?我该如何解决?
谢谢!
您的代码似乎没有任何问题,但请记住,这是一个递归函数,并且大多数语言都有一个深度有限的堆栈,如果您的输入足够大,您一定会到达该堆栈。对于 Java,请参阅:
- What is the maximum depth of the java call stack?
- How to predict the maximum call depth of a recursive method?
您可以做的是将您的方法从递归方法转变为迭代方法。有几种方法可以做到这一点。我刚刚在网上找到了几个例子:
有一些方法可以减少快速排序中的堆栈大小。
您可以每次都将较大的分区放在堆栈上,将其留在堆栈中,而较小的分区首先排序。这样可以防止过多的小分区一次堵塞堆栈。
您可以使用其他方法(例如插入排序)来对小分区进行排序,而不是使用快速排序。同样,这使许多小分区远离堆栈。您可以进行试验,但 15 - 20 个元素的区域通常算作一个 "small" 分区。
正如您所说,使用更好的方法来选择您的支点也会有所帮助。
如果我给出反向排序的数组,我的 QuickSort 实现会导致 Whosebug 错误。它适用于大约 1000 个项目,但对于 10000+ 个项目,我收到 Whosebug 错误。如果我得到错误,递归深度约为 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);
}
}
我的实现有问题吗?我该如何解决? 谢谢!
您的代码似乎没有任何问题,但请记住,这是一个递归函数,并且大多数语言都有一个深度有限的堆栈,如果您的输入足够大,您一定会到达该堆栈。对于 Java,请参阅:
- What is the maximum depth of the java call stack?
- How to predict the maximum call depth of a recursive method?
您可以做的是将您的方法从递归方法转变为迭代方法。有几种方法可以做到这一点。我刚刚在网上找到了几个例子:
有一些方法可以减少快速排序中的堆栈大小。
您可以每次都将较大的分区放在堆栈上,将其留在堆栈中,而较小的分区首先排序。这样可以防止过多的小分区一次堵塞堆栈。
您可以使用其他方法(例如插入排序)来对小分区进行排序,而不是使用快速排序。同样,这使许多小分区远离堆栈。您可以进行试验,但 15 - 20 个元素的区域通常算作一个 "small" 分区。
正如您所说,使用更好的方法来选择您的支点也会有所帮助。