合并排序返回全零

时间:2017-11-06 04:03:22

标签: c# mergesort

最终合并的输出是[0,0,0,0]。我认为它适用于数组左半部分的第一次合并,因为它正确地排列。我想帮忙调试一下。

static public void Main(string[] args)
{
    int[] input = { 4, 1, 3, 2};
    MergeSort(input, 0, input.Length - 1);
    for (int i = 0; i < input.Length; i++)
    {
        Console.WriteLine(input[i]);
    }
    Console.ReadLine();
}

static public void MergeSort(int[] input, int left, int right)
{
    if (left < right)
    { 
        int middle = (left + right) / 2;
        MergeSort(input, left, middle);
        MergeSort(input, middle + 1, right);
        Merge(input, left, (middle + 1), right);
    }
}

static public void Merge(int[] input, int left, int middle, int right)
{
    int tempindex = left;
    int[] tmp = new int[input.Length];
    int rightpointer = middle + 1;
    int leftpointer = left; 
    while (leftpointer <= middle && rightpointer <= right)
    {
        if (input[leftpointer] < input[rightpointer])
        {
            tmp[tempindex] = input[leftpointer];
            leftpointer++; 
        }
        else
        {
            tmp[tempindex] = input[rightpointer];
            rightpointer++;
        }
        tempindex++; 
    }
    while (leftpointer <= middle)
    {
        tmp[tempindex] = input[leftpointer];
        leftpointer++; 
        tempindex++;
    }
    while (rightpointer <= right)
    {
        tmp[tempindex] = input[rightpointer];
        right++;
        tempindex++;
    }

    for (int i = 0; i < tmp.Length; i++)
    {
        input[i] = tmp[i];
    }
}

2 个答案:

答案 0 :(得分:1)

略微更改代码以使其正常工作。请参考一些在线资源以更好地理解算法

private static void Merge(int[] input, int left, int middle, int right)
    {
        int[] temp = new int[input.Length];
        int rightpointer = middle - 1;
        int tempindex = left;
        int num = right - left + 1;

        while ((left <= rightpointer) && (middle <= right))
        {
            if (input[left] <= input[middle])
            {
                temp[tempindex] = input[left];
                tempindex++;
                left++;
            }
            else
            {
                temp[tempindex] = input[middle];
                tempindex++;
                middle++;
            }
        }

        while (left <= rightpointer)
        {
            temp[tempindex] = input[left];
            tempindex++;
            left++;
        }

        while (middle <= right)
        {
            temp[tempindex] = input[middle];
            tempindex++;
            middle++;
        }

        for (int i = 0; i < num; i++)
        {
            input[right] = temp[right];
            right--;
        }
    }

答案 1 :(得分:0)

我会尝试从更高层次的角度解释您的错误,而不是简单地发布您的代码的固定版本,我认为这是一个毫无意义且无益的练习,并且不会从更高层次的角度来解释您的错误。关于如何避免将来出现类似错误的一些建议。

您的代码中存在一些问题。它们分为三类:

  1. 关接一个。经典编程错误,在处理索引时,尤其是在处理这些边缘情况的边界值时,您应始终保持警惕。
  2. 简单的拼写错误。另一个经典编程错误。在这种情况下,当您复制并粘贴while循环,然后过度键入错误的变量名称时,可能会发生这种情况。你应尽量避免复制/粘贴;在经典合并排序的情况下,它实际上是不可避免的,所以你应该遵循推论规则:如果你是复制/粘贴,你必须在粘贴的代码中更改变量名称,双重和三重检查你的工作!
  3. 概念。这是&#34;经典&#34;从某种意义上说,程序员很难学习良好的高级概念化和抽象技能,但它以多种不同的方式表现出来(与前两种不同),因此提供一般性的方法更难建议。我最好的建议是多年前我的一位教授喜欢说:KISS,或者#34; keep it simple, stupid。&#34;或者我喜欢表达同样的事情:&#34;懒惰是好的&#34;。无论哪种方式,重点是尽可能地简化问题。如果你必须手工完成,请保持数据建模与您想要解决问题的方式接近,并确保如果必须手动完成,那么您最简单,最不能做到这一点。劳动密集型的方式。
  4. 那么,那些错误出现在哪里?从最后到第一......

    从概念上讲,代码中最大的问题是您的Merge()方法会分配一个input数组的完整大小的数组,即使您知道肯定该方法应该只考虑该数组的一个子集。 (你知道,因为这是该方法的其他三个参数的重点。)

    这导致该方法稍后出现问题,因为当您将tmp数组复制回input时,您只是盲目地写了一个循环来复制所有数据。即使该方法仅实际将值写入tmp数组的子集,也是如此。这最终是你输出全为零的原因。并非所有tmp数组中的值都已写入,因此它们的默认值仍为0,然后将这些值复制到input数组中,覆盖原始值你真的想要。

    如果您为tmp分配的数组只有足够大的数据来包含您在该特定调用期间实际操作的数据,那么如何在最后复制数据将会非常清楚。在最坏的情况下,你会得到一个IndexOutOfRangeException,这是发现错误的很多更好的方式,而不是在最后得到错误的输出(这是一个例子) Fail-fast原则,我喜欢的另一个概念。

    现在,关于那个错字。坦率地说,一旦你有数据触发它,你最终会发现这个错误。实际上,您的输入数据集并不存在,因为它永远不会进入错误所在的循环。但是,while方法中的上一个Merge()循环正在递增right变量而不是rightindex变量。一旦IndexOutOfRangeException变量变得过大,这将导致tempindex(因此,至少它会失败 - 快:) :)。

    FWIW,除了简单地确保你在复制/粘贴时对你的工作进行双重和三重检查,这是恕我直言的一个例子,为什么在像这样的简单循环中,实际上需要使用更多将后增量表达式放在数组中的简洁语法。这样做可以确保您递增的索引确保您用于索引数组的索引:

    while (rightpointer <= right)
    {
        tmp[tempindex++] = input[rightpointer++];
    }
    

    最后,关于你的逐个错误,这不仅可以避免在处理索引的边界值时非常小心,而且还要始终牢记一些心理表示(我喜欢可视化数据结构) )价值观如何相关。

    在这种特殊情况下,为middle方法的Merge()参数传递的值处理不一致。也就是说,在MergeSort()方法中,middle值明显用作&#34; left&#34;的上限。侧分区。这可以在对MergeSort()方法的递归调用中看到。但是当您拨打Merge()时,您可以为该通话的middle参数添加一个值。

    然而,在Merge()方法中,您仍然使用该参数来指示左侧分区的上限。当您将middle + 1作为该值传递时,您告诉Merge()方法将数组的不同部分视为左侧分区而不是您在递归调用{{1}时实际排序的部分在致电MergeSort()之前。这在算法的结果中具有明显且有害的后果。 :)

    如果你更加努力地想象这些分区,以及将它们定义到分区的变量的关系,你就不太可能首先编写错误的代码。

    以下是代码的更正版本,注释行显示了我所做的更改:

    Merge()

    最后,我还没有写任何有关调试的内容。但实际上,尝试修复代码的第一步是使用调试器逐步完成。编写代码时,您需要考虑代码要采取的各种操作和结果。当您调试代码时,您正在寻找的是任何违反这些期望的地方。

    如果你介入了代码,那么你会发现违反预期的第一件事就是将未初始化的static public void Main(string[] args) { int[] input = { 4, 1, 3, 2 }; MergeSort(input, 0, input.Length - 1); for (int i = 0; i < input.Length; i++) { Console.WriteLine(input[i]); } Console.ReadLine(); } static public void MergeSort(int[] input, int left, int right) { if (left < right) { int middle = (left + right) / 2; MergeSort(input, left, middle); MergeSort(input, middle + 1, right); // was: Merge(input, left, (middle + 1), right); Merge(input, left, middle, right); } } static public void Merge(int[] input, int left, int middle, int right) { // was: int tempindex = left; int tempindex = 0; // was: int[] tmp = new int[input.Length]; int[] tmp = new int[right - left + 1]; int rightpointer = middle + 1; int leftpointer = left; while (leftpointer <= middle && rightpointer <= right) { if (input[leftpointer] < input[rightpointer]) { tmp[tempindex] = input[leftpointer]; leftpointer++; } else { tmp[tempindex] = input[rightpointer]; rightpointer++; } tempindex++; } while (leftpointer <= middle) { tmp[tempindex] = input[leftpointer]; leftpointer++; tempindex++; } while (rightpointer <= right) { tmp[tempindex] = input[rightpointer]; // was: right++; rightpointer++; tempindex++; } for (int i = 0; i < tmp.Length; i++) { // was: input[i] = tmp[i]; input[left + i] = tmp[i]; } } 值从0数组复制到tmp阵列。你可能会或者可能没有像我一样修复那个特定问题 - 那时最方便的方法实际上就是简单地改变input循环的边界,只复制你实际写的那些元素值为(我修复它的方式,因为它使用较少的内存,因为它是编写代码的一个例子,以便在出现错误时,它可以更快地失败) - 但至少你会发现问题。