C++:合并排序合并数组

C++: Merge Sort Merging Arrays

本文关键字:合并 数组 排序 C++      更新时间:2023-10-16

当我运行代码时,我得到很多重复的数字和/或大的负数,当你把东西添加到数组中时,这些数字和/或大的负数通常就会出现。我相信问题是当我进行合并本身时。

void mergeSort( int list[], int lb, int ub )
{
    int mid;
    if ( lb < ub )
    {
        mid = (lb + ub) /  2;
        mergeSort(list, lb, mid);
        mergeSort(list, mid + 1, ub);
        myMerge(list, lb, mid , ub);
    }
}
template <class M>
void myMerge( M list[], int lb, int mid, int ub )
{
   int i, j;
   int size1 = mid - lb + 1;
   int size2 = ub - mid;
    M* tmpArray1 = new M[size1 + 1];
    M* tmpArray2 = new M[size2 + 1];
    for( i=0; i<size1; i++ )
    {
        tmpArray1[i] = list[lb + i - 1];
    }
    for( j=0; j<size2; j++ )
    {
        tmpArray2[j] = list[mid + j];
    }
    tmpArray1[size1 + 1] = INT_MAX;
    tmpArray2[size2 + 1] = INT_MAX;
    i = 0;
    j = i;
    for( int k=lb; k<ub; k++ )
    {
        if ( tmpArray1[i] <= tmpArray2[j] )
        {
            list[k] = tmpArray1[i];
                i++;
        }
        else
        {
            list[k] = tmpArray2[j];
            j++;
        }
    }
}

这可能是像迭代问题一样愚蠢的事情......有什么想法吗?

在这一行中:

    tmpArray1[i] = list[lb + i - 1];

你的意思肯定是这样的:

    tmpArray1[i] = list[lb + i];

否则,您将从给定的合并边界之外获取一个值,这将解释重复的数字。 写回列表时不使用该逻辑。

我假设mergeSort代码是正确的,这意味着ub应该是要排序的范围的最后一个索引。如果不是这种情况,mergeSort就被错误地实现了(merge仍然会如此,但方式略有不同)。

填充tmpArray1时,您可以从范围之前访问元素:

for( i=0; i<size1; i++ )
{
    tmpArray1[i] = list[lb + i - 1];
}

范围中的第一个元素是 list[lb] ,而不是 list[lb-1]

填充tmpArray2时,您会忽略范围末尾的一个元素:

for( j=0; j<size2; j++ )
{
    tmpArray2[j] = list[mid + j];
}

这应该在那里list[mid + 1 + j]

合并

时,不会将所有元素合并回来:

for( int k=lb; k<ub; k++ )
{
    if ( tmpArray1[i] <= tmpArray2[j] )
    {
        list[k] = tmpArray1[i];
            i++;
    }
    else
    {
        list[k] = tmpArray2[j];
        j++;
    }
}

这应该在循环控制中k <= ub

但是,最让我揉搓的是

tmpArray1[size1 + 1] = INT_MAX;
tmpArray2[size2 + 1] = INT_MAX;

如果数组包含 INT_MAX ,则必然会失败,如果元素类型为 long long .

使用

哨兵值来标记数组的末尾是不合理的,您应该使用索引来检测它。

你的算法有几个问题。

首先,它会导致内存泄漏,因为它分配了永远不会删除的数组。需要一些delete[]说明来解决问题。

其次,存在索引错误:有些索引变为负数,这肯定是你不希望的(例如,当你做tmpArray1[i] = list[lb + i - 1];时,因为lbi都可以是0)。

第三,你缺少一个基本步骤:你从不交换两个元素的值。你的递归步骤看起来不错,但递归必须在某个时候结束并做一些具体的事情(即当你的范围只跨越 2 个元素时)。您的 mergeSort() 函数拆分范围,只是递归地为第一个和第二个子范围调用自身,但在递归结束时对它们不执行任何操作。

第四,您没有正确处理两个子范围具有不同大小的情况(一个子范围可能比另一个子范围逐个元素大)。

以下是您应该如何修复代码(在GCC 4.7.2上测试):

template <class M>
void myMerge( M arr[], int lb, int mid, int ub )
{
   int i, j;
   int size1 = mid - lb + 1;
   int size2 = ub - mid;
    M* tmpArray1 = new M[size1];
    M* tmpArray2 = new M[size2];
    for( i=0; i<size1; i++ )
    {
        tmpArray1[i] = arr[lb + i]; // THIS NEEDED FIXING
    }
    for( j=0; j<size2; j++ )
    {
        tmpArray2[j] = arr[mid + 1 + j]; // THIS NEEDED FIXING AS WELL (add +1...)
    }
    i = 0;
    j = i;
    for( int k=lb; k<=ub; k++ )
    {
        if (i == size1) // HANDLES THE CASE WHEN FIRST RANGE IS SMALLER
        {
            arr[k] = tmpArray2[j];
            j++;
        }
        else if (j == size2) // HANDLES THE CASE WHEN SECOND RANGE IS SMALLER
        {
            arr[k] = tmpArray1[i];
            i++;
        }
        else if ( tmpArray1[i] < tmpArray2[j] )
        {
            arr[k] = tmpArray1[i];
            i++;
        }
        else
        {
            arr[k] = tmpArray2[j];
            j++;
        }
    }
    delete[] tmpArray1; // IMPORTANT! DON'T FORGET TO RELEASE
    delete[] tmpArray2; // THE MEMORY YOU ALLOCATE...
}
void mergeSort( int arr[], int lb, int ub )
{
    if (ub - lb > 1)
    {
        int mid = (lb + ub) /  2;
        mergeSort(arr, lb, mid);
        mergeSort(arr, mid + 1, ub);
        myMerge(arr, lb, mid, ub);
    } 
    else // DON'T FORGET TO ADD YOUR BASE STEP! (sort a trivial range of 2 elements)
    {
        if (arr[ub] < arr[lb])
        {
            int tmp = arr[ub];
            arr[ub] = arr[lb];
            arr[lb] = tmp;
        }
    }
}
// SOME TESTING...
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
int main()
{
    int numbers[] = { 8, 40, 1, 5, 0, 9, 6, 4, 3, -1, 5 };
    mergeSort(numbers, 0, 10);
    copy(begin(numbers), end(numbers), ostream_iterator<int>(cout, " "));
}