我在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;
}
}
答案 0 :(得分:3)
所以你有三个递归函数。让我们看一下575000个元素列表中最差情况的最大深度:
log_2(575000)
或大约20个堆栈帧。因此,当我们运行程序时,我们会得到有限的堆栈空间以适应所有堆栈帧。在我的计算机上,默认限制大约为10兆字节。
粗略估计每个堆栈帧占用32个字节。对于merge()
的情况,这意味着它将占用大约18兆字节的空间,这远远超出了我们的限制。
mergesort()
调用自身,只有20次迭代。这应该符合任何合理的限制。
因此,我的意思是merge()
和split()
不应该以递归方式实现(除非这种方式是尾递归并且优化已经开启)。
答案 1 :(得分:0)
有点晚了,但这是导致堆栈溢出的递归merge()。递归split()不是问题,因为它的最大深度是log2(n)。
因此只需要将merge()转换为迭代。
正如很久以前评论的那样,使用小型(25到32)指针数组的自下而上方法更简单,更快,但我不确定这对于为任务提供太多帮助会是一个问题。链接到wiki伪代码:
链接到工作C示例:
http://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists