MergeSort:我的问题是由不正确的 indeces 还是递归的实例变量引起的

问题描述

我目前正在阅读 Clrs,同时也在尝试学习 C++。正如您可以想象的那样,一切都很顺利!我一直在理解 MergeSort,但在过去的几天里取得了很好的进展,感觉我的代码就快完成了。 附注Java 是我的强项,所以如果我混淆了语言之间的词汇,我深表歉意!

我想问题是由以下几个问题之一引起的:
1)因为我还是 C++ 的新手,我读过在实例中使用类变量的方式很重要,以便在整个代码中应用数组的大小。话虽如此,我认为在我的递归过程中,我的 r 变量是一个只读常量;它使我的代码经历了导致问题的递归的额外迭代。
-我对此的解决方案是将该变量设置为一个新变量并使其根据需要适应数组。这仍然会导致错误解决方案!
2)我已经多次阅读了在 C++ 中不要使用数组的内容,尤其是在这种情况下,而是使用向量。鉴于我还没有完全了解向量,我觉得使用数组更舒服,并相信问题可能是由于我的程序中指针和引用的编码不正确。
3)我的最终假设也来自于阅读了很多类似问题的答案,即不在递归方法中使用实例变量。我很确定这在 Java 中不是问题,并且感觉这可能只是 C++ 中的一个大禁忌。

我见过无数的 MergeSort 类,并且非常有信心我的 mergeSort & merge 方法是完美的,这让我相信这个问题可能只是缺乏对 C++ 语法或规则的了解!任何意见或帮助将不胜感激!我经常得到的数组的最后 2 个结果是 [2,4,5,1] & [2,7,1]

#include <iostream>
#include <cmath>

using namespace std;

class MergeSort
{
    static const size_t r = 8;
    int A[r];

    public:
        MergeSort() : A{2,1,2,3,6}
        {
            printMergeSort(A,r);
            int p = 0;
            mergeSort(A,p,r);
            printMergeSort(A,r);
        }

    static void mergeSort(int *A,int p,int r)
    {
        if(p < r)
        {
            //int q = floor(((p + r) / 2)); 
            int q = ((p + r) / 2);
            mergeSort(A,q);
            mergeSort(A,q + 1,r);
            merge(A,q,r);
        }
    }

    static void merge(int *A,int q,int r)
    {
        int n1 = q - p + 1;
        int n2 = r - q;
        int L[n1+1];
        int R[n2+1];
        for(int i = 0; i < n1; i++) 
        {
            L[i] = A[p + i]; 
        }
        for(int j = 0; j < n2; j++)
        {
            R[j] = A[q + j + 1]; 
        }
        //L[n1 + 1] = std::numeric_limits<int>::max();
        L[n1] = INT_MAX;
        R[n2] = INT_MAX;
        int i = 0;
        int j = 0;
        for(int k = p; k <= r; k++)
        {
            if(L[i] <= R[j])
            {
                A[k] = L[i];
                i = i + 1;
            }
            else
            {
                A[k] = R[j];
                j = j + 1;
            }
        }
    }

    static void printMergeSort(int *A,size_t r)
    {
        for(int i = 0; i < r; i++)
        {
            cout << A[i] << " ";
        }
        cout << endl;
    }
};

int main()
{
    //MergeSort();
    MergeSort x;
    return 0;
}

解决方法

您走对了,学习实践算法的最佳方法是用编程语言实现它们。

关于您的问题,我认为问题出在您的合并功能上。 在合并函数中,您要做的是在两个较小的数组中移动并选择最小值并将其存储在临时寄存器中。

例如如果你有

{2,7} {4,6}

您想先选择“2”并增加指向第一个数组开头的指针,然后选择“4”和“6”,最后选择“7”。

我在此处发布了您的代码的更正版本,其中对变量命名进行了细微更改。 这里还有一些注意事项:

  1. constructor 不是调用函数的好地方。我以在主函数中调用合并排序和打印的方式修改了您的代码。

  2. 要找到mid,不需要使用floor。当您将两个整数相除时,结果也将是一个整数。即:(3+4)/2 = 3

    #include #包括

    using namespace std;
    const int INT_MAX = 2147483647;
    
    class MergeSort
    {
        static const size_t r = 8;
        int A[r];
    
        public:
            MergeSort() : A{2,4,5,7,1,2,3,6}
            {
                //printMergeSort(A,r);
                int p = 0;
                //mergeSort(A,p,r);
                //printMergeSort(A,r);
            }
    
            void mergeSort () { 
               mergeSortUtil (A,r-1);
            }
        static void mergeSortUtil(int *A,int p,int r)
        {
            if(p < r)
            {
                int mid = (p + (r-p) / 2);
                mergeSortUtil(A,mid);
                mergeSortUtil(A,mid + 1,r);
                merge(A,mid,r);
            }
        }
    
        static void merge(int *A,int low,int mid,int high)
        {
            int left=low; 
            int right = mid+1;
            int i=low;
    
            int res[high-low+1];
    
    
            while (left<=mid && right <=high) {
                if(A[left] < A[right]) {
                    res[i] = A[left];
                    left++;
                } else {
                    res[i]=A[right];
                    right++;
                }
                ++i;
    
            }
            while (left<=mid) {
               res[i] = A[left];
               left++; 
               i++;
            }
            while (right <=high) {
               res[i]=A[right];
               right++;
               i++;
            }
    
            for (int i=low; i<=high; ++i) {
    
              A[i]=res[i];
    
            }
    
        }
    
         void printMergeSort()
        {
            for(int i = 0; i < r; i++)
            {
                cout << A[i] << " ";
            }
            cout << endl;
        }
    };
    
    int main() {
        //MergeSort();
        MergeSort * x = new MergeSort();
        x->printMergeSort();
        x->mergeSort();
        x->printMergeSort();
        return 0;
    }
    
,

一个直接的问题是调用 merge(A,q,r),而 merge 将其参数作为 (int *A,int q,int r)。看看 pq 是如何混淆的?

另一个不太明显的问题是 r 的语义。初始调用传递 8,这意味着 r 超过范围结束,并且不属于该范围。然而,

        mergeSort(A,q);
        mergeSort(A,q + 1,r);

暗示第一个调用中的 q 确实属于。你应该下定决心。通常传递过去会导致更简单和更清晰的代码。考虑

        mergeSort(A,r);

    int n1 = q - p;
    int n2 = r - q;
    int L[n1];
    int R[n2];

(不再需要奇怪的 INT_MAX 赋值)。

当然,主合并循环是错误的。这在上面的答案中得到了解决。但是,为了强调过去的论点,请考虑

    while ((i < n1) && (j < n2)) {
        if(L[i] <= R[j])
        {
            A[k++] = L[i++];
        } else {
            A[k++] = R[j++];
        }
    }
    while (i < n1) {
        A[k++] = L[i++];
    }
    while (j < n2) {
        A[k++] = R[j++];
    }