合并排序:删除[]时的堆损坏

时间:2014-04-18 21:21:06

标签: c++ mergesort dynamic-memory-allocation delete-operator heap-corruption

处理一个类项目,我需要在其中实现一个Merge Sort来排序500,000个项目。 经过多次尝试,我尝试在线查找源代码并在此处找到一些:http://www.sanfoundry.com/cpp-program-implement-merge-sort/

我不得不改变代码以使用动态数组(对于大小)。当程序运行合并函数时,我使用正在合并的元素(或高)数创建一个新的动态数组。一旦函数完成排序并将它们合并到原始数组中,我就在新的动态数组上使用delete []。这是我发现“堆腐败检测”错误的地方。

这是代码(文本墙):

//Heap Sort

#include <iostream>
#include <fstream>
#include <sstream>
#include <ctime>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

//Function Prototypes
void mergesort(int *a, int low, int high);
void merge(int *a, int low, int high, int mid);


int main()
{
//Start with element 1 of the array
int line_no = 0;
int num;
int array_size = 500000;
int* num_array = new int[array_size];

//Open file for input
fstream in_file("CSCI3380_final_project_dataset.txt", ios::in);

//Test for file opening
if (!in_file)
{
    cout << "Cannot open words1.txt for reading" << endl;
    exit(-1);
}

//Read file
while(true)
{
    //Read one line at a time
    in_file >> num;

    //Test for eof
    if (in_file.eof())
      break;

    num_array[line_no] = num;

    //Increment array position
    line_no++;

}

//Close the file
in_file.close();

//Start Time
clock_t time_a = clock();   

//Run Sorting Algorithim
mergesort(num_array, 0, array_size-1);  

//End Time
clock_t time_b = clock();



//Elapsed Time
if (time_a == ((clock_t)-1) || time_b == ((clock_t)-1))
{
    cout << "Unable to calculate elapsed time" << endl;
}
else
{
    int total_time_ticks = time_b - time_a;
    cout << "Elapsed time: " << total_time_ticks << endl;
}

delete[] num_array; 

return 0;
}

void mergesort(int *a, int low, int high)

{

int mid;

if (low < high)

{

    mid=(low+high)/2;

    mergesort(a,low,mid);

    mergesort(a,mid+1,high);

    merge(a,low,high,mid);

}

return;

}

void merge(int *a, int low, int high, int mid)

{


//--------------------------Create new array-------------------------------

int* sort_array = new int[high];

//--------------------------New Array Created-----------------------------

int i, j, k;

i = low;

k = low;

j = mid + 1;

while (i <= mid && j <= high)

{

    if (a[i] < a[j])

    {

        sort_array[k] = a[i];

        k++;

        i++;

    }

    else

    {

        sort_array[k] = a[j];

        k++;

        j++;

    }

}

while (i <= mid)

{

    sort_array[k] = a[i];

    k++;

    i++;

}

while (j <= high)

{

    sort_array[k] = a[j];

    k++;

    j++;

}

for (i = low; i < k; i++)

{

    a[i] = sort_array[i];

}

//---------------------------Delete the New Array--------------------

delete[] sort_array;

//--------------------------Oh No! Heap Corruption!------------------

}

2 个答案:

答案 0 :(得分:2)

我会饶你&#34;你应该使用矢量&#34;,&#34;你应该使用智能指针&#34;等等你应该是,而且我会留在那。关于你的实际问题......

您正在编写一个超过阵列分配空间的内容。分配的大小为high

int* sort_array = new int[high];

意味着您只能从0..(high-1)取消引用。但是这个:

while (j <= high)
{
    sort_array[k] = a[j];
    k++;
    j++;
}

是一个保证写入sort_array[high]的位置,因此会调用未定义的行为


不同的方法

Mergesort是关于div-2分区的。你知道这个。你可能考虑的是C和C ++都精确地执行指针算术 ,因此你只需要mergesort()的两个参数:一个基地址和一个长度。其余的可以用指针数学来处理:

考虑一下:

void mergesort(int *a, int len)
{
    if (len < 2)
        return;

    int mid = len/2;    
    mergesort(a, mid);
    mergesort(a + mid, len-mid);
    merge(a, mid, len);
}

一个merge实现,如下所示:

void merge(int *a, int mid, int len)
{
    int *sort_array = new int[ len ];
    int i=0, j=mid, k=0;

    while (i < mid && j < len)
    {
        if (a[i] < a[j])
            sort_array[k++] = a[i++];
        else
            sort_array[k++] = a[j++];
    }

    while (i < mid)
        sort_array[k++] = a[i++];

    while (j < len)
        sort_array[k++] = a[j++];

    for (i=0;i<len;++i)
        a[i] = sort_array[i];

    delete[] sort_array;
}

main()调用,如下所示。注意:我已经删除了文件i / o而不是随机生成,只是为了更容易测试:

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cstdio>
using namespace std;

//Function Prototypes
void mergesort(int *a, int len);
void merge(int *a, int mid, int len);

int main()
{
    std::srand((unsigned int)std::time(nullptr));

    // Start with element 1 of the array
    int array_size = 500000;
    int* num_array = new int[array_size];
    std::generate_n(num_array, array_size, std::rand);

    // Start Time
    clock_t time_a = clock();

    // Run Sorting Algorithim
    mergesort(num_array, array_size);

    // End Time
    clock_t time_b = clock();

    //Elapsed Time
    if (time_a == ((clock_t)-1) || time_b == ((clock_t)-1))
    {
        cout << "Unable to calculate elapsed time" << endl;
    }
    else
    {
        int total_time_ticks = time_b - time_a;
        cout << "Elapsed time: " << total_time_ticks << endl;
    }

    delete[] num_array;

    return 0;
}

结果是经过的时间:

Elapsed time: 247287

更高效

到目前为止,您已经看到除了序列之外,您最多还需要N个空间。最顶层的合并应该足够证据。您可能考虑的是实际上完全您需要的空间,并且您可以预先分配它并在整个算法中使用它(如果您愿意)。您可以保留mergesort()的当前截留,但我们将使用前端加载器将其包装起来,前端加载器分配我们曾经需要一次的所有空间:< / p>

// merges the two sequences  a[0...mid-1] and a[mid...len-1]
//  using tmp[] as the temporary storage space
static void merge_s(int *a, int *tmp, int mid, int len)
{
    int i=0, j=mid, k=0;

    while (i < mid && j < len)
    {
        if (a[i] < a[j])
            tmp[k++] = a[i++];
        else
            tmp[k++] = a[j++];
    }

    while (i < mid)
        tmp[k++] = a[i++];

    while (j < len)
        tmp[k++] = a[j++];

    for (i=0;i<len;++i)
        a[i] = tmp[i];
}

static void mergesort_s(int *a, int *tmp, int len)
{
    if (len < 2)
        return;

    int mid = len/2;
    mergesort_s(a, tmp, mid);
    mergesort_s(a + mid, tmp+mid, len-mid);
    merge_s(a, tmp, mid, len);
}

void mergesort(int *a, int len)
{
    if (len < 2)
        return;

    int *tmp = new int[len];
    mergesort_s(a,tmp,len);
    delete [] tmp;

}

这导致经过的时间:

Elapsed time: 164704

比以前好多了。祝你好运。

答案 1 :(得分:1)

使用一对函数控制合并的方向可以避免WhozCraig代码示例中显示的复制步骤(注意 - 自下而上合并仍然会更快)。

注意 - 我不建议使用WhozCraig或我的代码示例,因为这些方法可能不在您的课程中,并且它应该是根据您在课堂上教授的内容编写的代码。我不知道你的班级是否涵盖了自下而上的合并排序,所以我没有发布它的例子。

mergesort_s(int *a, int *tmp, int len)
{
// ...
    mergesort_atoa(a, tmp, 0, len);
// ...
}

mergesort_atoa(int *a, int *tmp, int low, int end)
{
    if((end - low) < 2){
        return;
    }
    int mid = (low + end) / 2;
    mergesort_atot(a, tmp, low, mid);
    mergesort_atot(a, tmp, mid, end);
    merge_s(tmp, a, low, mid, end);
}    

mergesort_atot(int *a, int *tmp, int low, int end)
{
    if((end - low) < 2){
        tmp[0] = a[0];
        return;
    }
    int mid = (low + end) / 2;
    mergesort_atoa(a, tmp, low, mid);
    mergesort_atoa(a, tmp, mid, end);
    merge_s(a, tmp, low, mid, end);
}    

void merge_s(int *src, int *dst, int low, int mid, int end)
{
    int i = low;                    // src[] left  index
    int j = mid;                    // src[] right index
    int k = low;                    // dst[]       index
    while(1){                       // merge data
        if(src[i] <= src[j]){       // if src[i] <= src[j]
            dst[k++] = src[i++];    //   copy src[i]
            if(i < mid)             //   if not end of left run
                continue;           //     continue (back to while)
            while(j < end)          //   else copy rest of right run
                dst[k++] = src[j++];
            return;                 //     and return
        } else {                    // else src[i] > src[j]
            dst[k++] = src[j++];    //   copy src[j]
            if(j < end)             //   if not end of right run
                continue;           //     continue (back to while)
            while(i < mid)          //   else copy rest of left run
                dst[k++] = src[i++];
            return;                 //     and return
        }
    }
}