【问题标题】:Merge Sort Not Working?合并排序不起作用?
【发布时间】:2015-09-17 18:55:24
【问题描述】:

我在让我的合并排序功能与教授给我的规范配合使用时遇到了一些问题。一直盯着 VS 和 Google 看,试图找出这个人。

提供的算法:

arrayFunctions.h

template<class T>
void printArray(T arr[], int numElements)
{
    cout << "(";

    for (int i = 0; i < numElements; i++)
    {

        cout << arr[i];

        if (i < numElements - 1)
            cout << ", ";
    }

    cout << ")" << "\n\n";
}

template <class T>
void setArray(T to[], T from[], int size)
{
    for (int i = 0; i < size; i++)
        to[i] = from[i];
}

template <class T>
void setArray(T to[], T from[], int size1, int size2)
{
    int size = size1;
    if (size2 < size1) size = size2;

    setArray(to, from, size);
}

主要

const int NUM = 5;

int originalArray[NUM] = { 4, 2, 5, 3, 1 };
int newArray[NUM];

cout << "Original:\n";
printArray(originalArray, NUM); //prints an array with formatting

// Merge Sort
setArray(newArray, originalArray, NUM); //set's newArray to the same values of originalArray
mergeSort(newArray, 0, NUM - 1);

cout << "Merge Sort:\n";
printArray(newArray, NUM);

pause();

运行main时的输出为:

原文: (4, 2, 5, 3, 1)

合并排序: (0, 0, 0, -33686019, 1)

合并:

template <class T>
void merge(T L[], int lowerBound, int mid, int upperBound)
{
    // Get size for new arrays
    int size1 = mid - lowerBound;
    int size2 = upperBound - mid;

    // Create Temporary Arrays
    T * tmp1 = new T[size1 + 1]();
    T * tmp2 = new T[size2 + 1]();


    // Populate both arrays from original
    for (int i = 0; i < size1; i++)
        tmp1[i] = L[lowerBound + i];

    for (int j = 0; j < size2; j++)
        tmp2[j] = L[mid + j];

    tmp1[size1] = numeric_limits<T>::max();
    tmp2[size2] = numeric_limits<T>::max();

    int i = 0;
    int j = i;

    for (int k = lowerBound; k < upperBound; k++)
    {
        if (tmp1[i] <= tmp2[j])
        {
            L[k] = tmp1[i];
            i++;
        }
        else
        {
            L[k] = tmp2[j];
            j++;
        }
    }

    delete[] tmp1;
    delete[] tmp2;
}

合并排序:

template<class T>
void mergeSort(T L[], int lowerBound, int upperBound)
{
    if (lowerBound < upperBound)
    {
        int mid = (lowerBound + upperBound) / 2;

        mergeSort(L, lowerBound, mid);
        mergeSort(L, mid + 1, upperBound);

        merge(L, lowerBound, mid, upperBound);
    }
}

那么……我做错了什么?非常感谢您朝正确的方向前进。

【问题讨论】:

  • 当您使用调试器时,哪些行或变量导致了问题?
  • setArray 是做什么的?你测试过吗?它运行正常吗?
  • 投票结束——没有证据表明 O.P. 使用了调试器。我不想浪费时间尝试编译代码并在其上使用调试器来进行 O.P>
  • 为什么你的哨兵infinity被注释掉了?你不能用std::numeric_limits&lt;T&gt;::max()吗?我认为这就是问题所在。由于您没有哨兵,因此您正在将垃圾复制到size1+1size2+1 的位置。
  • 我没有收到任何编译或致命的运行时错误,所以我不确定。

标签: c++ algorithm sorting mergesort


【解决方案1】:

这是一个简单的错误。

T * tmp1 = new T[size1 + 1]();
...
tmp1[size1 + 1] = numeric_limits<T>::max();
           ^^^

数组索引从0n-1,而不是n

【讨论】:

  • 谢谢,刚刚听懂了。
  • 哦,来吧,关于原始new 没什么好说的? :)
  • @TemplateRex 很明显这是功课,学习如何处理原始指针是有用的,即使你不再使用它。
【解决方案2】:
// tmp1[size1 + 1] = (infinity?)
// tmp2[size2 + 1] = (infinity?)

这是破坏合并的代码部分,考虑一下如果您有两个列表中的元素多于 1 个,那么它们应该会发生什么,请参阅:

// Create Temporary Arrays
T * tmp1 = new T[size1 + 1]();
T * tmp2 = new T[size2 + 1]();

这意味着它们对于 2 个值可能看起来像这样

foo = [1,2,?]
bar = [3,4,?]

问号将是一个数字,但你无法知道是什么,如果你在循环内运行几次比较,你会说 i = 2, j == 0 为简单起见,现在您尝试进行比较:

if (foo[2] <= bar[0])

这和

是一样的
if (? <= 3)

意味着你有一个未定义的行为,更糟糕的是你可能会去 i = 3 并开始查看随机内存。 所以总而言之,(无穷大?)以某种聪明的方式。

【讨论】:

  • “总之,(无穷大?)以某种巧妙的方式”——这可能就是分配numeric_limits&lt;T&gt;::max() 的行试图做的事情。
  • @davmac 那是因为他改变了问题。在我建议numeric_limits&lt;T&gt;::max() 之前,它是(infinity?),并且这些行被注释掉了。
  • @davmac 是的,但由于这看起来像是一项学校作业,我认为放弃正确答案可能是徒劳的。 ;)
  • @davmac 没问题! :) 我解释是因为我实际上认为他不应该改变它,因为这是问题的一部分。
【解决方案3】:

在C++的merge函数中,L[]的右半部分以mid + 1开头,所以第二个populate循环应该是:

    for (int j = 0; j < size2; j++)
        tmp2[j] = L[mid + j + 1];

在提供的算法中,索引从 1 到 n,因此第一个填充循环是 TMP1[i] ← L[lowerBound + i - 1]。对于 C++,索引从 0 到 n-1,因此 C++ 首先填充循环: tmp1[i] = L[lowerBound + i];是正确的,但是第二个循环需要改成 tmp2[j] = L[mid + j + 1]; .

【讨论】:

    【解决方案4】:

    抱歉近两周忘记回复了:/

    这是一个错误,但我追踪了它们并让它正常工作。 感谢所有提供帮助的人。

    改变了

    int size1 = mid - lowerBound;
    int size2 = upperBound - mid;
    

    int size1 = mid - lowerBound + 1;
    int size2 = upperBound - mid;
    

    改变了

    for (int j = 0; j < size2; j++)
        tmp2[j] = L[mid + j];
    

    for (int j = 0; j < size2; j++)
        tmp2[j] = L[mid + j + 1];
    

    改变了

    for (int k = lowerBound; k < upperBound; k++)
    {
        if (tmp1[i] <= tmp2[j])
        {
            L[k] = tmp1[i];
            i++;
        }
        else
        {
            L[k] = tmp2[j];
            j++;
        }
    }
    

    for (int k = lowerBound; k <= upperBound; k++)
    {
        if (tmp1[i] <= tmp2[j])
        {
            L[k] = tmp1[i];
            i++;
        }
        else
        {
            L[k] = tmp2[j];
            j++;
        }
    }
    

    【讨论】:

      【解决方案5】:

      问题 1:哨兵

      infinity 被注释掉是问题之一。

      // tmp1[size1] = (infinity?)
      // tmp2[size2] = (infinity?)
      

      它被用作哨兵,所以当你到达tmp1(或tmp2)的最后一个位置时,它会确保你从另一个数组tmp2(或@ 987654326@)。您可以在此处使用std::numeric_limits&lt;T&gt;::max() 代表inf

      @Petter 很好地描述了为什么会出现此问题。

      问题 2:初始化

      您的另一个问题似乎是在合并数组之前的初始化:

      int i = 1;
      int j = i;
      

      伪代码从1开始索引,而它应该从0开始。

      问题 3:索引

      正如@MarkRansom 所指出的,索引范围应该是从0size1,而不是size1 + 1

      tmp1[size1 + 1] = numeric_limits<T>::max();
      

      【讨论】:

        猜你喜欢
        • 2016-08-19
        • 2018-12-04
        • 2015-02-09
        • 2016-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-05
        相关资源
        最近更新 更多