1

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

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++;
        }
    }
}

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

4

3 回答 3

2

在这一行:

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

你的意思肯定是这样的:

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

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

于 2013-01-22T23:51:19.343 回答
2

我假设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如果数组包含或更大的值,如果元素类型是 eg ,那肯定会失败long long

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

于 2013-01-22T23:52:46.367 回答
1

你的算法有一些问题。

首先,它会导致内存泄漏,因为它分配了永远不会删除的数组。需要一些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, " "));
}
于 2013-01-23T00:21:32.967 回答