迭代合并的C ++实现由于堆栈溢出导致大输入大小的崩溃

时间:2014-04-04 15:13:02

标签: c++ algorithm sorting stack-overflow mergesort

我对堆栈溢出没有多少经验,我认为它们是由超过某个递归深度的递归函数引起的,为什么它们会在这个迭代实现的合并排序中出现!

    #include<iostream>
    #include <stdlib.h>
    #define SIZE 3000000

    int L[2500002];
    int R[2500002];

    using namespace std;

    int min(int a, int b) {
        return !(b<a) ? a : b;
    }

    void Merge(int data[], int p, int q, int r)
    {
        if (q >= SIZE)q = (r + p) / 2;
        int sizeL = q - p + 2;
        int sizeR = r - q + 1;

        for (int i = 0; i < sizeL - 1; i++)
            L[i] = data[i + p];
        for (int i = 0; i < sizeR - 1; i++)
            R[i] = data[i + q + 1];

        int max;
        if (L[sizeL - 2]>R[sizeR - 2])
            max = L[sizeL - 2] + 1;
        else
            max = R[sizeR - 2] + 1;
        L[sizeL - 1] = R[sizeR - 1] = max;

        int indexL = 0, indexR = 0;
        for (int i = p; i <= r; i++){
            if (L[indexL] <= R[indexR]){
                data[i] = L[indexL];
                indexL++;
            }
            else{
                data[i] = R[indexR];
                indexR++;
            }
        }


    }

    void MergeSort(int data[], int p, int r)
    {
        for (int i = 1; i< SIZE; i *= 2)
            for (int j = 0; j < SIZE; j += 2 * i)
                Merge(data, j, j + i - 1, min((j + 2 * i - 1), SIZE - 1));
    }
    /*****************************************************************************/

    bool IsSorted(int data[], int size)
    {
        int i;

        for (i = 0; i<(size - 1); i++)
        {
            if (data[i] > data[i + 1])
                return false;
        }
        return true;
    }

    int main()
    {
        int data[SIZE];
        for (int i = 0; i < SIZE; i++)
            data[i] = rand();
        MergeSort(data,0,SIZE-1);
        if(IsSorted(data, SIZE))
            cout << "Sorted correctly";
        else
            cout << "incorrect sorting";
        getchar();
        return 0;
    }

3 个答案:

答案 0 :(得分:3)

int main()
{
    int data[SIZE];
    ...

在堆栈上声明数组。堆栈大小有限制。它因操作系统和配置而异,但它可能很容易小于12 MB,即您要求的数量(假设为32位整数)。尝试使用std::vector在堆上分配您的数组。

编译器无法就此问题向您发出警告,因为堆栈大小是由OS在加载程序时设置的。你可以重新配置你的操作系统默认使用一个小堆栈,之前工作的程序会突然开始溢出。

如果您有兴趣了解操作系统如何检测堆栈溢出,请查看虚拟内存,MMU和Guard页面。

答案 1 :(得分:1)

不确定这是否是问题,但your merge sort seems buggy

使用无符号整数而不是签名。

答案 2 :(得分:0)

class cSort_t
{
public:
    template <typename T>
    static void insertionSort (T* _array, const unsigned size);

    template <typename T>
    static void mergeSort(T *data, const unsigned size);

protected:
    cSort_t ();
    ~cSort_t ();
    cSort_t (const cSort_t& ref);
    const cSort_t& operator= (const cSort_t& ref);

private:
    template <typename T>
    _inline static void copyElements(T * dst,T * src, unsigned size);
};

//----------------- Insertion Sort --------------------
// standard insertion sort routine
template <typename T>
void cSort_t::insertionSort (T* _array, const unsigned size)
{ 
    T _temp;
    for (int unsorted = 1; unsorted<(int)size; ++unsorted)
    {
        _temp = *(_array+unsorted);
        for (int index = unsorted-1; index>=0; --index)
        {
            if (_temp < *(_array+index))
            {
                *(_array+index+1) = *(_array+index);
                if (0==index)
                     *_array = _temp;
            }
            else
            {
                *(_array+index+1) = _temp;
                break;
            }
        }
    }
}

// copyElements - simple loop unrolling of data array copy
// to remove some overhead of looping.
template <typename T>
__inline void cSort_t::copyElements(T * dst, T * src, int size)
{
   while(size > 3)
   {
       *(dst+0) = *(src+0);
       *(dst+1) = *(src+1);
       *(dst+2) = *(src+2);
       *(dst+3) = *(src+3);
       dst+=4;
       src+=4;
       size-=4;
   };
   while(size > 0)
   {
       *dst++ = *src++;
       size--;
   };
}

//----------------- Merge Sort Function ----------------------
// A bottom's up iterative merge sort.
// Internal working buffer(temp[size]).
// Simple ping-pong buffering to reduce copy backs.
// Stable sort, maximum sort complexity=O(n*log n).
// Faster then most sorts for general purpose use.
// Returns the sorted result in the input buffer.
// Use: cSort_t::mergeSort(data_array, size_of_array);
// The reason for the speed up is simpler loops, pointer manipulation,
// no copy backs, recursive calls and storage re-creation. 
template <typename T>
void cSort_t::mergeSort(T *data, unsigned size)
{
    // use insertion sort for sorting small arrays
    if(size < 32) 
    {
        insertionSort (data, size);
        return;
    }

    unsigned int i,sp0c,sp1,sp1c;
    T *p0,*p1,*tmp,*tp1,*tp2;
    T *temp = new T [size];      // create an internal working temp

    // do easy first pass of bottoms up merge sort
    // calculate the number of passes used by algorithm to 
    // make the final sorted data end up back in input array to save moves.
    // by using either an initial in-place compare swap or a out-place compare move.
    i=0; for(unsigned j=1; j<size; j <<= 1) i++;   // 2**i >= size
    if(i&1)
    {
        // calculated odd number of passes
        // do the in-place compare and swap
        // to make last pass sorted data to end in the input buffer. 
        p0 = data;
        for(i=1; i<size; i+=2, p0+=2)
        {
            if(*p0 > *(p0+1)) {*temp = *p0; *p0 = *(p0+1);  *(p0+1) = *temp;}
        }
        tp1 = data;
        tp2 = temp;
    }
    else 
    {
        // calculated even number of passes
        // do an out of place compare and move 
        // to make last pass sorted data to end in the input buffer.
        p0 = data;
        p1 = temp;
        for(i=1; i<size; i+=2, p0+= 2, p1+= 2)
        {
            if(*p0 <= *(p0+1)) {*p1 = *p0; *(p1+1) = *(p0+1);}
            else {*p1 = *(p0+1); *(p1+1) = *p0;}
        }
        if(size&1) *p1 = *p0; // move last "odd" word
        tp1 = temp;
        tp2 = data;
    }
    // do more complex part of the bottoms up merge sort
    // instead of using a half way split use a binary split
    // only complication added is handing the right side element's remainder.
    //
    //      group 1          group n-1         group n 
    //  [ 5  9 | 7  2 ]   [ 3  6 | 4  9 ]   [ 7  6 | 5 ]
    //    ^      ^          ^      ^          ^      ^
    //   lsp    rsp        lsp    rsp        lsp    rsp        
    //
    p0 = tp1;   // p0 points to data elements be sorted 
    tmp = tp2;  // tmp points to where to put results this pass
    // for each pass of the bottoms up merge sort
    for(i=2; i<size; i<<=1)
    {
        p1 = p0 + i;     // calculate first groups' right side pointer
        sp1 = size - i;  // and number of elements past that pointer(inclusive)
        // do merge for all groups in this merge pass   
        for(;;)
        {
            sp0c = i;   // number of left side elements in group 
            sp1c = i;   // number of right side elements in group
            // on last group left and right lengths are different.
            if(sp1 < i) sp1c = sp1;
            // do a merge pass on a group
            for(;;)
            {
                if(*p0 <= *p1)   // simple compare merge pass
                {
                    *tmp++ = *p0++;   // move a left side element
                    if((--sp0c) == 0) break; 
                }
                else
                {
                    *tmp++ = *p1++;   // move a right side element
                    if((--sp1c) == 0) break;
                }
            }
            // we always have a few unmerged elements on left or right side  
            if(sp0c != 0)
            {
                // move what's remaining of left side's elements
                do {*tmp++ = *p0++;} while((--sp0c) != 0);
            }
            else
            {
                // move what's remaining of right side's elements
                do {*tmp++ = *p1++;} while((--sp1c) != 0);
            }
            p0 = p1;    // adjust left side and right side pointer
            p1 += i;    // for next group 
            if(sp1 <= (i<<1)) break;
            sp1 -= (i<<1);
        }
        // All groups processed except for possibly the last group 
        // because it may not have had a right side to sort.
        // if any remaining data copy it(partially sorted in previous passes)
        if(sp1 > i)
        {
            copyElements(tmp, p0, (sp1-i));
        }   
        // swap working pointers instead of copying temp array back to data array
        p0 = tp2;
        tmp = tp2 = tp1;
        tp1 = p0;
    }
    // delete internal working temp, sorted data should be back in input.
    delete [] temp;
}