合并排序实现输出错误结果

时间:2019-07-10 20:56:13

标签: c++14 mergesort

我试图在C ++ 14中实现合并排序。我已经编写了完整的代码,并针对逻辑错误多次对其进行了校对,但找不到任何内容。但是代码输出的数组排序错误,有时甚至包含重复的元素和/或从来没有输入到数组中的元素。

这是我的代码:

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::endl;
using std::vector;
void merge_sort(vector<int>&, int, int);
void print_vector(vector<int>&);
void merge(vector<int>&, int, int, int);

int main() {
    int arr_len = 0;
    cout << "Enter the length of the array to be sorted: " << endl;
    cin >> arr_len;

    vector<int> arr(arr_len);

    cout << "Enter the elements of the array: " << endl;
    for (int i = 0; i < arr_len; i++) {
        int buff;
        cin >> buff;
        arr[i] = buff;
    }

    cout << "The elements entered in the unsorted vector are: " << endl;
    print_vector(arr);

    merge_sort(arr, 0, arr_len - 1);

    cout << "After Merge sorting, the elements in the vector are: " << endl;
    print_vector(arr);

    return 0;
}

void print_vector(vector<int>& arr) {
    for (auto itr = arr.begin(); itr != arr.end(); ++itr) {
        cout << *itr << " ";
    }
    cout << endl;
}

void merge_sort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int mid = low + (high - low) / 2;           // used this instead of (low + high) / 2 to avoid overflow problems
        merge_sort(arr, low, mid);                  // recursive call to merge_sort with high = mid's updated value
        merge_sort(arr, mid + 1, high);
        merge(arr, low, mid, high);                 // call to merge to sort and merge the fragmented arrays.
    }
}

void merge(vector<int>& arr, int low, int mid, int high) {
    int l_arr_len = mid - low + 1;
    int r_arr_len = high - mid;
    vector<int> l_arr(l_arr_len);
    vector<int> r_arr(r_arr_len);

    for (int i = 0; i < l_arr_len; i++) {        // initialise elements of temp_arr1 (l_arr) to 0.
        l_arr[i] = 0;
    }

    for (int i = 0; i < r_arr_len; i++) {        // initialise elements of temp_arr2 (r_arr) to 0.   
        r_arr[i] = 0;
    }

    for (int i = 0; i < l_arr_len; i++) {        // transfer elements from arr to l_arr, upto length of the fragmented l_arr.
        l_arr[i] = arr[low + i];
    }

    for (int i = 0; i < r_arr_len; i++) {        // transfer remaining elements from arr to r_arr, upto length of the fragmented r_arr.
        r_arr[i] = arr[mid + 1 + i];
    }

    int i = 0, j = 0, k = 0;
    while (i < l_arr_len && j < r_arr_len) {            // compare and replace elements in the mother array arr
        if (l_arr[i] <= r_arr[j]) {                      // smallest element goes first
            arr[k++] = l_arr[i++];
        } else {
            arr[k++] = r_arr[j++];
        }
    }

    while (i < l_arr_len) {                  // write remaining elements in the left_arr fragment to mother array arr
        arr[k++] = l_arr[i++];
    }

    while (j < r_arr_len) {                  // write remaining elements in the left_arr fragment to mother array arr
        arr[k++] = r_arr[j++];
    }
}

对于元素[2, 9, 4, 5, 7]的输入数组,正确的排序结果应为:[2, 4, 5, 7, 9]

但是我的实现输出:[5, 5, 7, 7, 9]。我不知道重复元素来自何处以及为什么它们替换了原始元素。为了尽量方便SO社区的访问,我尝试在几乎要添加的注释中添加注释,但是其中一些可能是多余的。

由于我不明智,请帮助我更正我的代码。您可以指出哪里出了问题,如果方便的话,在哪里。

提前谢谢! :)

3 个答案:

答案 0 :(得分:1)

在合并功能中,将k初始化为低值,而不是零:

    int i = 0, j = 0, k = low;

我刚刚注意到肯尼·奥斯特罗姆(Kenny Ostrom)的评论可能与进行此更改有关。

答案 1 :(得分:1)

其他人发现了主要问题,k必须初始化为low而不是0

还有更多问题您应该看看:

  • 数组索引值和大小的正确类型是size_t,而不是int,它的范围可能小得多。

  • 传递最后一个元素的索引而不是排除的上限会产生繁琐的代码,需要进行索引调整。

  • 不需要初始化临时向量,您应该复制内容,或者更好地从数组切片构造它们。

  • print_vector应该引用const

此处是修改版本:

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::endl;
using std::vector;
void merge_sort(vector<int>&, size_t, size_t);
void merge(vector<int>&, size_t, size_t, size_t);
void print_vector(const vector<int>&);

int main() {
    size_t arr_len = 0;
    cout << "Enter the length of the array to be sorted: " << endl;
    cin >> arr_len;

    vector<int> arr(arr_len);

    cout << "Enter the elements of the array: " << endl;
    for (size_t i = 0; i < arr_len; i++) {
        cin >> arr[i];
    }

    cout << "The elements entered in the unsorted vector are: " << endl;
    print_vector(arr);

    merge_sort(arr, 0, arr_len);

    cout << "After Merge sorting, the elements in the vector are: " << endl;
    print_vector(arr);

    return 0;
}

void print_vector(const vector<int>& arr) {
    for (auto itr = arr.begin(); itr != arr.end(); ++itr) {
        cout << *itr << " ";
    }
    cout << endl;
}

void merge_sort(vector<int>& arr, size_t low, size_t high) {
    if (high - low > 1) {
        size_t mid = low + (high - low) / 2;    // used this instead of (low + high) / 2 to avoid overflow problems
        merge_sort(arr, low, mid);              // recursive call to merge_sort with high = mid's updated value
        merge_sort(arr, mid, high);
        merge(arr, low, mid, high);             // call to merge to sort and merge the fragmented arrays.
    }
}

void merge(vector<int>& arr, size_t low, size_t mid, size_t high) {
    size_t l_arr_len = mid - low;
    size_t r_arr_len = high - mid;
    vector<int> l_arr(l_arr_len);
    vector<int> r_arr(r_arr_len);

    for (size_t i = 0; i < l_arr_len; i++) {    // transfer elements from arr to l_arr, upto length of the fragmented l_arr.
        l_arr[i] = arr[low + i];
    }
    for (size_t i = 0; i < r_arr_len; i++) {    // transfer remaining elements from arr to r_arr, upto length of the fragmented r_arr.
        r_arr[i] = arr[mid + i];
    }

    size_t i = 0, j = 0, k = low;
    while (i < l_arr_len && j < r_arr_len) {    // compare and replace elements in the mother array arr
        if (l_arr[i] <= r_arr[j]) {             // smallest element goes first
            arr[k++] = l_arr[i++];
        } else {
            arr[k++] = r_arr[j++];
        }
    }
    while (i < l_arr_len) {                  // write remaining elements in the left_arr fragment to mother array arr
        arr[k++] = l_arr[i++];
    }
    while (j < r_arr_len) {                  // write remaining elements in the left_arr fragment to mother array arr
        arr[k++] = r_arr[j++];
    }
}

答案 2 :(得分:0)

在我的代码中,特别是在void merge(...)函数中,我将变量k初始化为0。该变量k应该根据其值来跟踪已排序元素在母数组arr中的放置位置。

导致的结果是,无论是母数组arr的哪个片段,当将不同数组片段中的元素排序和合并在一起时,k都被硬编码为{{1 }},0的第一个(已排序)元素被满足条件的任何数组片段中的下一个元素替换。因此,数组最终将被填满,程序将正确退出,但是在将部分排序的数组中注入重复项之前不会退出,这些重复项来自arr元素被以下两个数组片段之一替换:{{ 1}}或arr[k++]。当然,根据合并排序的原理,这是错误的,因为要保持阵列元素的合并模式。可能很难将其形象化,因此这是“合并排序”的表示形式,以供参考:

Merge Sort Schematic Representation. Notice the arrows and how it all comes together.

因此,这里是解决方法:l_arr应该初始化为r_arr ,而不是 k。这样,就可以保持合并排序的模式,将适当片段中的适当元素递归组合在一起,以形成排序后的数组。

Kenny Ostromrcgldr建议使用此修复程序。非常感谢他们俩!