【问题标题】:Merge Sort: Heap Corruption on delete[]合并排序:删除时的堆损坏 []
【发布时间】:2014-04-18 21:21:05
【问题描述】:

在我需要实现合并排序来对 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!------------------

}

【问题讨论】:

  • 你喜欢空白! :D
  • C++ 的第一条规则:不要自己管理内存。
  • 我认为您正在访问 1 超过您在合并中创建的 sort_array 的末尾。您无法访问 sort_array[high]。
  • "合并排序" - 这应该是自上而下还是自下而上的合并排序?虽然自上而下的归并排序是一种常见的课堂练习,但大多数“现实世界”归并排序都是自下而上的。在任何一种情况下,您都可以一次性分配第二个临时数组,并从一个数组到另一个数组并返回合并步骤,方向取决于自上而下的递归级别或自下而上的迭代。对于自上而下,递归部分需要两个“姐妹”函数,一个以原始数组中的数据结束,另一个以临时数组中的数据结束,每个“姐妹”函数调用另一个。
  • 对于自下而上,您将 n 个元素的数组视为大小为 1 的 n 组。您将偶数组和奇数组从一个数组合并到另一个数组,每次完成一次传递时方向交替.完成传递后(将所有元素合并到“其他”数组),然后将大小加倍 (2, 4, ... ) 并交换指针以改变方向。当组大小大于或等于数组大小时进行排序。

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


【解决方案1】:

我不会说“你应该使用向量”、“你应该使用智能指针”等。你应该这样做,我就这样说吧。关于你的实际问题....

您正在写一个过去分配的数组空间。分配的大小是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() 保留当前的诱捕,但我们将使用前端加载器将其包裹起来,该加载器分配我们将需要的所有空间一次

// 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

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

【讨论】:

    【解决方案2】:

    使用一对函数来控制合并的方向,可以避免 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
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-11-24
      • 1970-01-01
      • 2013-06-04
      • 2013-04-20
      • 2013-01-23
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多