Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 排序时堆栈溢出(合并排序和快速排序)_C++_Sorting_Stack Overflow_Mergesort - Fatal编程技术网

C++ 排序时堆栈溢出(合并排序和快速排序)

C++ 排序时堆栈溢出(合并排序和快速排序),c++,sorting,stack-overflow,mergesort,C++,Sorting,Stack Overflow,Mergesort,我们应该比较每种输入10000次的速度。它们都是自己工作的,但当我将它们添加到程序中时,我认为合并排序可能会占用大量空间,因此一旦到达快速排序,我总是会得到一个未处理的异常:StackOverflow。是否有人知道这个问题的解决方案,使合并排序不会占用很多空间(如果是问题的话)?具体来说,异常是在快速排序的分区函数中抛出的 #include <iostream> #include <fstream> #include <ctime> using namespa

我们应该比较每种输入10000次的速度。它们都是自己工作的,但当我将它们添加到程序中时,我认为合并排序可能会占用大量空间,因此一旦到达
快速排序
,我总是会得到一个
未处理的异常: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