【问题标题】:understanding this mergesort algorithm?理解这个归并排序算法?
【发布时间】:2014-01-13 10:43:34
【问题描述】:

我有以下代码

void mergesort(int size, int ar[], int temp[])
{
    if(size <=1)
    {
        return;}
    else
    {
        int i = 0;
        int mid = size/2;

        int *left = &ar[0];
        int *right = &ar[mid];
        int *rend = &ar[size];
        int *lend = right;

        mergesort(mid,left,temp);
        mergesort(size-mid,right,temp);

        for(i=0; i<size;i++)
        {
            if(left < lend && (*left < *right || right >= rend))
            {
                temp[i] = *left++;
            }
            else
            {
                temp[i] = *right++;
            }
        }
        for(i = 0; i < size; i++)
        {
            ar[i] = temp[i];
        }
    }
}

我不明白这个 if 语句是如何工作的:

if(left < lend && (*left < *right || right >= rend))
{
   temp[i] = *left++;
}
else
{
   temp[i] = *right++;
}

你能告诉我那里发生了什么吗?为什么我们必须比较地址? (左 = 撕裂)

这是从 main 调用 mergesort 函数的方式

const int SIZE = 8;
int array[SIZE] = {3,6,1,-9,0,2,4,5};
int temp[SIZE];

mergesort(SIZE, array,temp);

【问题讨论】:

  • 因为数组存储为顺序内存地址?
  • 考虑如果你不这样做会发生什么。什么会阻止读取(子)数组的末端?
  • 也就是说,这段代码对我来说看起来很糟糕。如果right 到达数组的末尾,那么它仍然会在*left &lt; *right 中被取消引用,这会调用未定义的行为。
  • @OliCharlesworth 是的,应该是 (right >= rend || *left
  • @AbhishekBansal:只有在left == lend检查;)

标签: c++ algorithm recursion mergesort divide-and-conquer


【解决方案1】:

为了在左序列或右序列之间选择输入,您需要知道序列是否耗尽。您可以通过测试指针来检查它是否超出有效范围。 left &lt; lend 返回 true 如果左边的序列没有用尽,right &gt;= rend 返回true 如果右边的序列用尽。因此,if 将在左侧序列未用尽时采用,并且左侧项目小于右侧项目或右侧序列已用完。

如果您遵循标准库迭代器约定,则永远不会在比较中检查 &lt;&gt;left != lendright == rend 在您发布的代码中同样有效。

附:两个不属于您的问题的想法:

  1. 如 cmets 中所述,比较中潜伏着未定义的行为。在检查右侧指针的值之前,您需要测试右侧是否已耗尽。
  2. 如果使用正确的比较,归并排序自然是稳定的。您希望从左侧序列中获取任何关系。在您对ints 进行排序的情况下,这并不重要,因为您无法将一个相同的int 与另一个进行区分,但如果这只是较大结构的一部分中的一个关键,它可能会非常重要。只需将&lt; 替换为&lt;= 即可。

综合以上所有建议,您最终得到:

if (left != lend && (right == rend || *left <= *right))

【讨论】:

  • 谢谢!但我继续您的建议并更改了if 测试条件。它工作正常,但我只是想知道那里发生了什么,所以我只是将std::cout &lt;&lt; *right 放在第一个 for 循环中,在 if 语句上方。在某些时候,*right 的值为 (-858993460)。我发布的if 测试条件也有同样的问题。当传递的数组按降序排列时,这种情况经常发生。这是否意味着我必须重新编写整个代码?但它可以毫无问题地对数字进行排序。
  • @DJK,当*right 超出数组末尾时,您将遇到未定义的行为。什么事情都可能发生;你可能会得到一个无意义的数字,你的程序可能会崩溃,或者demons could fly out of your nose。这就是为什么我建议重新安排if 条件,以便首先进行越界测试。没关系,因为|| 是短路的,这意味着如果前半部分为真,它将跳过条件的后半部分。
【解决方案2】:

Hariprasad 是对的。然后你做一个比较left &lt; lend,你比较一些元素的地址。因为数组是一组概念性的地址,所以指针可以像索引和迭代器一样使用。然后您进行比较*left &lt; *right,例如,您当然比较值,并确定它们中的较小值。

【讨论】:

    【解决方案3】:

    这段代码存在多个问题:

    1. int *rend = &amp;ar[size]; -- 读取超出数组 ar 的末尾
    2. (*left &lt; *right || right &gt;= rend) -- 我认为这应该是 (right &gt; (rend - 1) || (*left &gt; *right)) 以避免阅读超出 ar 的结尾。

    您可以通过单步执行初始数组大小为 2 或 3 的代码来自行解决问题。这两种方法都可以快速暴露问题。我在脑海中快速完成了这项工作,但没有使用调试器进行测试。注意事项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-04
      • 2021-04-29
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 2015-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多