【问题标题】:How do I prevent stack overflow when using merge sort on large amounts of data?对大量数据使用归并排序时如何防止堆栈溢出?
【发布时间】:2018-02-08 18:47:46
【问题描述】:

当我使用 100、1000 或 10000 个长链表进行测试时,我有以下合并排序算法。但是,当使用 100000 或 1000000 个长链表时,它会在包含 Node->Next=Merge(Front,Back->Next,Type); 的行上返回分段错误。这让我相信这是堆栈溢出而不是分段错误。根据 gdb 在错误发生时堆栈非常满。我无法找到一种方法来准确检查调用堆栈中有多少项以给出准确的数字。任何有关修改合并排序以处理大量数据的帮助将不胜感激。

struct IntList
{
int Value;
int Frequency;
struct IntList* Next;
struct IntList* Prev;
};//Struct for Integer Linked List
void SortList(struct IntList** Values,enum SortBy Type)
{
    struct IntList* Head = *Values;
    if(Head==NULL||Head->Next==NULL)
    {
        return;
    }//If Base Case
    struct IntList* Front;
    struct IntList* Back;
    Split(Head,&Front,&Back);//Splits Linked List
    SortList(&Front,Type);
    SortList(&Back,Type);//Recursively Sorts
    *Values=Merge(Front,Back,Type);//Merges Halves
    return;
}

void Split(struct IntList* Head,struct IntList** Front,struct IntList** Back)
{
    struct IntList* Fast;
    struct IntList* Slow;
    if (Head==NULL||Head->Next==NULL)
    {
        *Front=Head;
        *Back=NULL;
    }//If Length <2
    else
    {
        Slow=Head;
        Fast=Head->Next;
    }
    while(Fast!=NULL)
    {
        Fast=Fast->Next;
        if(Fast!=NULL)
        {
            Fast=Fast->Next;
            Slow=Slow->Next;
        }
    }//Find Midpoint
    *Front=Head;
    *Back=Slow->Next;
    Slow->Next=NULL;//Breaks Link
    return;
}


struct IntList* Merge(struct IntList* Front,struct IntList* Back,enum SortBy Type)
{
    if(Front==NULL)
    {
        return Back;
    }
    if (Back==NULL)
    {
        return Front;
    }//Base Cases

    struct IntList* Node;
    if(Type==DATA)
    {
        if(Front->Value <= Back->Value)
        {
           Node=Front;
           Node->Next=Merge(Front->Next,Back,Type);
        }
        else
        {
            Node=Back;
            Node->Next=Merge(Front,Back->Next,Type);
        }//Takes Greatest Value for Sorted List
    }//If Sorting by Data
    if(Type==FREQUENCY)
    {
        if(Front->Frequency < Back->Frequency)
        {
           Node=Front;
           Node->Next=Merge(Front->Next,Back,Type);
        }
        else
        {
            Node=Back;
            Node->Next=Merge(Front,Back->Next,Type);
        }//Takes Greatest Frequency for Sorted List
    }//If Sorting by Frequency
    return(Node);

【问题讨论】:

  • 停止使用递归合并列表;迭代地做。
  • 嗯,你可以在你的主机文件中阻止它,你可以使用家长控制应用程序......哦!你的意思是 literal 堆栈溢出!好吧,在这种情况下,user2357112 是对的,您可能已经达到了递归限制。 ;-)
  • 使用递归可以产生优雅的代码,但是您在这里亲眼看到它的麻烦和局限性。在gdb 中的段错误(或任何断点)处,您可以输入bt(回溯的缩写),它会打印出调用堆栈...无法quiiite 告诉您是否知道这一点已经。

标签: c stack-overflow mergesort


【解决方案1】:

这个最简单的答案是不使用递归。

但是,如果您对使用递归一无所知,您可以通过将函数中使用的临时变量移动到函数范围之外或声明它们 static 来限制堆栈上的内容,以便它们可以被重用而不是每次调用合并时为它们腾出空间(如果您对多线程应用程序感兴趣,可能会产生不利影响)。看起来您没有任何可以安全执行此操作的变量。

你还可以用另一个结构体封装参数,这样你就不必将三个指针传递给新的堆栈调用,即,一个参数占用的空间小于 3。

类似:

struct IntList* Merge(struct mergeData* data)

还有adjusting the stack size 的方法,以便您的应用程序可以处理您期望的数据集。

总的来说,这是递归的限制。如果您处理资源有限的嵌入式系统(例如我自己),那么您通常会避免使用它。

【讨论】:

  • 你也可以用另一个struct封装参数,这样你就不用传入三个指针到新的栈调用IE上,一个参数占用的空间小于3。 i> --- 什么?你确实意识到你必须在调用者堆栈上为结构腾出空间?
  • 在堆中分配是另一种选择。
【解决方案2】:

如果你想使用递归,最好尝试用尾调用的形式来表达(这样递归调用返回后除了返回之外什么都不做)。这样,大多数编译器会将尾调用优化为简单的跳转,而不使用任何堆栈空间。对于你的 Merge 函数,它变成了这样:

void Merge(struct IntList **merged, struct IntList* Front,struct IntList* Back,enum SortBy Type)
{
    if(Front==NULL) {
        *merged = Back;
    } else if (Back==NULL) {
        *merged = Front;
    } else if(Type==DATA) {
        if(Front->Value <= Back->Value) {
            *merged = Front;
            Merge(&Front->Next, Front->Next, Back, Type);
        } else {
            *merged = Back;
            Merge(&Back->Next, Front, Back->Next,Type);
        }//Takes Greatest Value for Sorted List if Sorting by Value
    } else if(Type==FREQUENCY) {
        if(Front->Frequency < Back->Frequency) {
            *merged = Front;
            Merge(&Front->next, Front->Next, Back, Type);
        } else {
            *merged = Back;
            Merge(&Back->Next, Front, Back->Next, Type);
        }//Takes Greatest Frequency for Sorted List
    }//If Sorting by Frequency
}

如果您的编译器不支持尾递归优化,您可以通过将函数体设为循环来自行完成:

void Merge(struct IntList **merged, struct IntList* Front,struct IntList* Back,enum SortBy Type)
{
  while(Front || Back) {
    if(Front==NULL) {
        *merged = Back;
        Back = NULL;
    } else if (Back==NULL) {
        *merged = Front;
        Front = NULL
    } else if(Type==DATA) {
        if(Front->Value <= Back->Value) {
            *merged = Front;
            merged = &Front->Next;
            Front = Front->Next;
        } else {
            *merged = Back;
            merged = &Back->Next;
            Back = Back->Next;
        }//Takes Greatest Value for Sorted List if Sorting by Value
    } else if(Type==FREQUENCY) {
        if(Front->Frequency < Back->Frequency) {
            *merged = Front;
            merged = &Front->Next;
            Front = Front->Next;
        } else {
            *merged = Back;
            merged = &Back->Next;
            Back = Back->Next;
        }//Takes Greatest Frequency for Sorted List
    }//If Sorting by Frequency
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-25
    • 2013-11-15
    • 2017-06-26
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-28
    相关资源
    最近更新 更多