合并排序导致堆栈溢出?

时间:2015-02-22 21:04:25

标签: c++ linked-list stack-overflow mergesort

我在C ++中为链接列表编写了mergesort()。问题是我的教授提供了一个非常大的列表(长度为575,000)的测试代码。这会导致我的函数出现堆栈溢出错误,因为它是递归写入的。

所以我的教授希望我们用迭代而不是递归来编写它。我想问一下我的代码是否有什么问题导致堆栈溢出?

我的代码:

typedef struct listnode {
    struct listnode * next;
    long value; 
} LNode;

LNode* mergesort(LNode* data) {
    if(data == NULL || data->next == NULL) {
        return data;
    }else {
        LNode* s = split(data);

        LNode* firstSortedHalf = mergesort(data);
        LNode* secondSortedHalf = mergesort(s);

        LNode* r = merge(firstSortedHalf, secondSortedHalf);
        return r;
    }
}

LNode* split(LNode* list) {
    if(list) {
        LNode* out = list->next;

        if(out) {
            list->next = out->next;
            out->next = split(out->next);
        }
        return out;
    }else {
        return NULL;
    }
}

LNode* merge(LNode* a, LNode* b) {
    if(a == NULL)
        return b;
    else if(b == NULL)
        return a;

    if(a->value < b->value) {
        a->next = merge(a->next,b);
        return a;
    }else {
        b->next = merge(a, b->next);
        return b;
    }
}

2 个答案:

答案 0 :(得分:3)

所以你有三个递归函数。让我们看一下575000个元素列表中最差情况的最大深度:

  • merge():这看起来迭代整个列表。所以575000堆栈帧。
  • split():这看起来是成对遍历整个列表。所以~250000堆栈帧。
  • mergesort():这看起来以分裂方式迭代。所以log_2(575000)或大约20个堆栈帧。

因此,当我们运行程序时,我们会得到有限的堆栈空间以适应所有堆栈帧。在我的计算机上,默认限制大约为10兆字节。

粗略估计每个堆栈帧占用32个字节。对于merge()的情况,这意味着它将占用大约18兆字节的空间,这远远超出了我们的限制。

mergesort()调用自身,只有20次迭代。这应该符合任何合理的限制。

因此,我的意思是merge()split()不应该以递归方式实现(除非这种方式是尾递归并且优化已经开启)。

答案 1 :(得分:0)

有点晚了,但这是导致堆栈溢出的递归merge()。递归split()不是问题,因为它的最大深度是log2(n)。

因此只需要将merge()转换为迭代。

正如很久以前评论的那样,使用小型(25到32)指针数组的自下而上方法更简单,更快,但我不确定这对于为任务提供太多帮助会是一个问题。链接到wiki伪代码:

this issue

链接到工作C示例:

http://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists