代码之家  ›  专栏  ›  技术社区  ›  Dustin H

在对大量数据使用合并排序时,如何防止堆栈溢出?

  •  1
  • Dustin H  · 技术社区  · 9 年前

    当我测试100、1000或10000个长链表时,我有以下合并排序算法。但是,它在包含的行上返回分段错误 Node->Next=Merge(Front,Back->Next,Type); 当使用100000或1000000个长链表时。这让我相信这是堆栈溢出,而不是分段错误。根据发生错误时的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);
    
    2 回复  |  直到 9 年前
        1
  •  1
  •   Chris Dodd    9 年前

    如果你想使用递归,最好用尾部调用的形式来表达它(这样递归调用返回后除了返回之外什么都不做)。这样,大多数编译器将把尾部调用优化为一个简单的跳转,并且不使用任何堆栈空间。为了您的 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
      }
    }
    
        2
  •  0
  •   Kyle s    9 年前

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

    static 因此,它们可以重用,而不是每次调用merge时都为它们腾出空间(如果您对多线程应用程序感兴趣,则可能会产生不利影响)。看起来你没有任何可以安全使用的变量。

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

    struct IntList* Merge(struct mergeData* data)
    

    还有一些方法 adjusting the stack size 以便应用程序能够处理预期的数据集。

    推荐文章