C++ 排序时堆栈溢出(合并排序和快速排序)
我们应该比较每种输入10000次的速度。它们都是自己工作的,但当我将它们添加到程序中时,我认为合并排序可能会占用大量空间,因此一旦到达C++ 排序时堆栈溢出(合并排序和快速排序),c++,sorting,stack-overflow,mergesort,C++,Sorting,Stack Overflow,Mergesort,我们应该比较每种输入10000次的速度。它们都是自己工作的,但当我将它们添加到程序中时,我认为合并排序可能会占用大量空间,因此一旦到达快速排序,我总是会得到一个未处理的异常:StackOverflow。是否有人知道这个问题的解决方案,使合并排序不会占用很多空间(如果是问题的话)?具体来说,异常是在快速排序的分区函数中抛出的 #include <iostream> #include <fstream> #include <ctime> using namespa
快速排序
,我总是会得到一个未处理的异常:StackOverflow
。是否有人知道这个问题的解决方案,使合并排序不会占用很多空间(如果是问题的话)?具体来说,异常是在快速排序的分区函数中抛出的
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int *L = new int[n1];
int *R = new int[n2];
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int partition(int arr[], int start, int end) { //start is 0 and end is counter-1
int pivot = start; //remember start here
int imp = arr[end];
for (int i = start; i < end; i++) { //remember this is start and end;
if (arr[i] <= imp) {
int temp = arr[i];
arr[i] = arr[pivot];
arr[pivot] = temp;
pivot++;
}
}
int p = arr[pivot];
arr[pivot] = arr[end];
arr[end] = p;
return pivot;
}
void quicksort(int arr[], int start, int end) {
if (start < end) {
int index = partition(arr, start, end);
quicksort(arr, start, index - 1);
quicksort(arr, index + 1, end);
}
}
int main() {
clock_t timereq;
double time_taken;
ifstream in("input3.txt");
int size;
in >> size;
int num;
int *arrq = new int[size];
int i = 0;
while (!in.eof()) {
in >> num;
arrq[i] = num;
i++;
}
timereq = clock();
mergeSort(arrq, 0,size-1);
timereq = clock() - timereq;
time_taken = double(timereq) / CLOCKS_PER_SEC; /// double(CLOCKS_PER_SEC);
cout << time_taken << endl;
timereq = clock();
quicksort(arrq, 0,size-1);
timereq = clock() - timereq;
time_taken = double(timereq) / CLOCKS_PER_SEC; /// double(CLOCKS_PER_SEC);
cout << time_taken << endl;
for (i = 0; i < size; i++) {
cout << arrq[i] << endl;
}
}
您应该真正遵循上面评论中给出的建议,通过使用stack
数据结构重新设计代码,直接解决问题的根源(有限的堆栈大小),从而特别避免内存耗尽的递归
然而,原则上,您也可以走捷径,采用更脏、更快的解决方案:只需向编译器添加标志,让它增加堆栈的大小
如果使用gcc,则可以通过插入-Wl,--stack,
键(如果从提示符处编译)来完成此操作。
上面的
键可以是大于当前堆栈大小的任意大小,例如-Wl,--stack,100000000
(9个零)
如果您使用的是IDE,我碰巧知道如何在代码块上执行此操作:转到设置->编译器->全局编译器设置->链接器设置->在“其他链接器选项”字段下添加上面的行
另请参见快速排序的问题在于其更糟糕的情况行为:
- 如果分区函数不将数据集分成平衡的两半,则复杂性可以达到O(N2),而不是平均O(N.log(N))
- 在您的情况下,最坏的情况发生在列表已经排序时:pivot将数组拆分为
N-1
和1
部分,导致递归发生N
次,可能对默认堆栈深度来说太多了
- 基准测试中存在一个逻辑错误,导致每次都出现这种最坏情况:您测量
mergeSort()
排序arrq
ad的时间,然后对同一数组上的quicksort
执行相同的操作,该数组刚刚按mergeSort
排序。您应该复制原始数组并将其传递给快速排序
,但还必须修复快速排序
,以避免堆栈溢出
通过将快速排序
函数更改为在较小的一半上递归,然后在较大的一半上迭代,可以解决此问题:
void快速排序(int-arr[],int-start,int-end){
while(开始<结束){
int index=分区(arr、start、end);
if(索引-开始<结束-索引){
快速排序(arr、start、索引-1);
开始=索引+1;
}否则{
快速排序(arr,索引+1,结束);
结束=指数-1;
}
}
}
这将解决堆栈溢出错误,但不会降低时间复杂性。您需要为此更改分区函数,例如,如果默认选择导致病态拆分,则随机选择一个pivot值。当您使用调试器运行程序时,您看到了什么?这正是调试器的用途。如果您不知道如何使用调试器,这是一个很好的机会,可以学习如何使用调试器一次运行一行程序,监视所有变量及其值的变化,并分析程序的逻辑执行流。了解如何使用调试器是每个C++开发人员所需的技能,没有例外。在调试器的帮助下,您应该能够快速找到您编写的此程序和所有未来程序中的所有错误,而无需向任何人寻求帮助。您应该在循环中使用std::stack
和适当的输入参数结构,而不是递归调用,以避免堆栈溢出。堆栈大小非常有限。@Sam好吧,调试器将向您显示funcrion被多次调用,并且在某个点停止。仅凭这些信息不太可能解决问题,除非在递归调用结束条件中遗漏了一个愚蠢的错误。@πάνταῥεῖ - 任何值得使用的调试器都将允许检查堆栈帧。我也不例外,我有导致堆栈溢出的bug。调试器向我显示了堆栈帧、每个递归调用的参数,并基于此可以识别递归逻辑中的缺陷。@Sam对我来说也是如此。但有时事实证明,该缺陷根本就是使用递归函数调用。我总是更喜欢使用循环和堆栈,不仅它更易于阅读和理解,而且它对诸如可用的本地调用堆栈大小之类的限制也更为健壮。qsort被排序的输入不仅仅是“可能是测试用例之一”。您可以在它们的主函数中看到这一点:它们在同一个数组上一个接一个mergesort运行qsort,因此除非它们的mergesort被破坏,否则它们的qsort总是得到排序的输入。只在较小的一半上递归的好主意,我想我以前没见过。
8
3 1 4 1 5 9 2 6