【问题标题】:Cannot implement merge sort in C++无法在 C++ 中实现归并排序
【发布时间】:2016-09-17 11:07:59
【问题描述】:

我是算法新手,我一直在尝试让合并排序工作,但它不会给出正确的输出。没有编译错误,但我猜它只是某处有缺陷,在输出中显示随机值作为排序数组。

void merge_sort(int[], int, int);
void merge(int[], int, int, int);
void printarray(int[], int);

int main() {
    int Arr[100], num_of_elements;
    cout << "Enter the number of elements (max 100): ";
    cin >> num_of_elements;
    cout << "Enter array elements: \n";
    for (int i = 0;i < num_of_elements;++i)
        cin >> Arr[i];
    merge_sort(Arr, 0, num_of_elements - 1);
    cout << "\nAfter Sorting (by Merge Sort):\n";
    printarray(Arr, num_of_elements);
    cout << endl;
    return 0;
}

void merge_sort(int arr[], int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        merge_sort(arr, left, mid);
        merge_sort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }  
}

void merge(int arr[], int left, int mid, int right) {
    int i, j, k;

    /* Calculate the lengths of the subarrays and copy the elements into them */
    int lenght_left = mid - left + 1;
    int length_right = right - mid;
    int *leftarray = new int[lenght_left];
    int *rightarray = new int[length_right];
    for (i = 0;i < lenght_left;++i)
        leftarray[i] = arr[left + i];
    for (j = 0;j < length_right;++j)
        rightarray[j] = arr[mid + 1 + j];

    /* Reordering the elements in the original array */
    for (k = left, i = 0, j = 0;k <= right;++k) {
        if (leftarray[i] <= rightarray[j])
            arr[k] = leftarray[i++];
        else
            arr[k] = rightarray[j++];
    }  

    /* Copy remaining elements into the array */
    while (i < lenght_left)
        arr[k] = leftarray[i++];
    while (j < length_right)
        arr[k] = rightarray[j++];
    delete[](leftarray);
    delete[](rightarray);
}

void printarray(int arr[], int num) {
    cout << "Displaying Elements in array: \n";
    for (int i = 0;i < num;i++)
        cout << arr[i] << "  ";
}

【问题讨论】:

  • 在创建Minimal, Complete, and Verifiable Example 时,务必使其完整,并显示您拥有的功能是如何使用的,以及您传递给它们的输入以及预期和实际输出。也请read about how to ask good questions.
  • 调试器是解决此类问题的正确工具。 询问 Stack Overflow 之前,您应该逐行浏览您的代码。如需更多帮助,请阅读How to debug small programs (by Eric Lippert)。至少,您应该 [编辑] 您的问题以包含一个重现您的问题的 Minimal, Complete, and Verifiable 示例,以及您在调试器中所做的观察。
  • showing random values in output as the sorted array. 是什么意思?您的意思是数组根本没有排序、部分排序或填充了垃圾数据?如果是最后一个,那么您在某处存在内存问题(=> 调试器)。您能否向我们展示您的合并排序函数的调用(包括您如何初始化数组并将其传递给您的函数)?
  • 虽然这与您的问题没有太大关系,但请学习如何正确使用 C++ 进行编码。您基本上是在用 C 编写代码,只需将 printf 替换为 cout 并将 malloc 替换为 new,然后将其称为 C++。不过,这在很多层面上都是错误的。无意冒犯,只是个人建议,但您确实应该学习正确的 C++ 做事方式,例如使用向量、STL 算法、模板等。如果您想编写高质量的代码,这些是必不可少的。 C++ 不是 C.
  • @adam10603 非常感谢您的建议,我现在刚刚学习 C++,还没有接触过模板和所有内容。您能提供一些参考链接,让我可以赶上并变得更好,更倾向于 C++ 吗?我会很感激的! :D

标签: c++ algorithm mergesort


【解决方案1】:

在您的merge 函数中:

/* Reordering the elements in the original array */
for (k = left, i = 0, j = 0; k <= right; ++k) {
//                           ^^^^^^^^^^^
// It should be i < lenght_left && j < length_right
    if (leftarray[i] <= rightarray[j])
        arr[k] = leftarray[i++];
    else
        arr[k] = rightarray[j++];
}

你用来控制循环的条件不对,条件应该是关于你的leftarrayrightarray,它应该是,当任何一个数组到达它们的末尾时,所以改变你的条件到i &lt; lenght_left &amp;&amp; j &lt; length_right

以及在同一函数中复制剩余元素时:

/* Copy remaining elements into the array */
while (i < lenght_left)
    arr[k] = leftarray[i++];
//     ^^^
while (j < length_right)
    arr[k] = rightarray[j++];
//     ^^^

这里,你忘了增加k,把它们改成k++

【讨论】:

    【解决方案2】:

    你犯了几个错误:

    1. 你需要检查你在合并时没有超过数组的长度

      即代替:

      for (k = left, i = 0, j = 0;k <= right;++k)
      

      你应该有:

      for (k = left, i = 0, j = 0;k <= right && i <lenght_left && j<length_right;++k)
      
    2. 您在添加剩余元素时忘记增加数组计数器。 即:

      while (i < lenght_left)
          arr[k] = leftarray[i++];
      while (j < length_right)
          arr[k] = rightarray[j++];
      

      你应该有:

      while (i < lenght_left)
          arr[k++] = leftarray[i++];
      while (j < length_right)
          arr[k++] = rightarray[j++];
      

    【讨论】:

    • 谢谢!有效。虽然我只是想知道为什么不检查 ij 小于各自的长度是必要的。 k 的循环应该只针对 merge() 方法中子数组中的元素数量运行?
    • 你能回答吗?
    • 想想case左边部分是:10 11 12,右边部分是1 2 3。然后会发生的是你将在arr中添加10 11 12你会检查偏移量将超出范围的左侧数组。
    • 但是这个条件i &lt;lenght_left &amp;&amp; j&lt;length_right 将是错误的,因为任何一个部分都已经到达了它们的末尾,并且不会有任何元素的越界访问。
    猜你喜欢
    • 2012-08-15
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多