CRLS合并排序边界代码对C代码的理解

时间:2019-04-03 22:29:41

标签: c sorting mergesort

void merge(int A[], int p, int q, int r) {
    int *tmpL, *tmpR;
    int boundary;
    int n1, n2;
    int i, j, k;

    n1 = q - p + 1;
    n2 = r - q;

    tmpL = (int *)malloc(sizeof(int) * (n1 + 1));
    tmpR = (int *)malloc(sizeof(int) * (n2 + 1));

    for (i = 0; i < n1; i++)
        tmpL[i] = A[p + i];
    for (j = 0; j < n2; j++)
        tmpR[j] = A[q + j + 1];

    boundary = tmpL[n1 - 1] > tmpR[n2 - 1] ? tmpL[n1 - 1] + 1 : tmpR[n2 - 1] + 1;
    tmpL[n1] = boundary;
    tmpR[n2] = boundary;

    i = 0;
    j = 0;

    for (k = p; k <= r; k++) {
        if (tmpL[i] <= tmpR[j]) {
            A[k] = tmpL[i];
            i++;
        } else {
            A[k] = tmpR[j];
            j++;
        }
    }

    free(tmpL);
    free(tmpR);
}
void merge_sort(int A[], int p, int r) {
    int q;

    if (p < r) {
        q = (p + r) / 2;
        merge_sort(A, p, q);
        merge_sort(A, q + 1, r);
        merge(A, p, q, r);
    }
}

我无法完全理解这个无限边界代码boundary = tmpL[n1 - 1] > tmpR[n2 - 1] ? tmpL[n1 - 1] + 1 : tmpR[n2 - 1] + 1;

谢谢 https://i.stack.imgur.com/UmyUg.png(蓝色圆圈)

这是一个条件语句A> B? C:D。 如果A> B为真,则评估C,否则评估D。 但是我仍然不了解边界部分。 这与添加两个while循环来处理时(其中一半有剩余元素并将它们附加到新数组的末尾)一样吗?

如果我不将它们初始化为无限边界,则可能会给我带来分割错误。

2 个答案:

答案 0 :(得分:0)

merge()应该合并A中两个已排序的运行,从A [p]到A [q],以及从A [q + 1]到A [r](含)。创建了TmpL和TmpR,每个都在末尾留出1个额外元素的空间,以用作前哨值,该值大于TmpL或TmpR中的任何值。三元语句将边界设置为TmpL和TmpR中最后一个值中的较大者,然后将该值加1以创建存储在TmpL和TmpR末尾的标记值。这样就无需检查索引“ i”或“ j”以查看是否已达到TmpL或TmpR的末尾,在这种情况下,其余的TmpR或TmpL将被复制回A []。

对于大多数编程语言,该代码可以仅将边界设置为INT_MAX或包含文件limit.h中的其他最大值之一(或者对于C ++为climits),而不使用三元语句:

http://www.cplusplus.com/reference/climits

如果排序浮动或加倍,则可以将边界设置为无穷大。

出现分段错误的原因是,如果没有前哨值,则代码可能会超出导致故障的TmpL或TmpR的结尾。

此排序方法的一个问题是A []可能已经包含最大可能值,在这种情况下,该方法将失败。对于整数,将1加到最大值将换成最小值。

答案 1 :(得分:0)

代码对mergesort使用一种通用方法,其中复制两个子数组,并在末尾添加一个额外的元素,其值设置为大于两个数组的最大值。

语句boundary = tmpL[n1 - 1] > tmpR[n2 - 1] ? tmpL[n1 - 1] + 1 : tmpR[n2 - 1] + 1;尝试将值boundary计算为1加上tmpLtmpR的最大值,取决于哪个更大。它使用三元表达式,大致相当于编写:

    if (tmpL[n1 - 1] > tmpR[n2 - 1])
        boundary = tmpL[n1 - 1] + 1;
    else
        boundary = tmpR[n2 - 1] + 1;

然后,合并循环可以使用单个测试k <= r停止循环,并且当{{时,i等于n1j等于n2 1}}到达k

这种方法在很多方面都被打破了:

  • 如果任何一个子数组包含最大值r + 1,则INT_MAX的计算将溢出并导致未定义的行为。即使溢出不会引起致命的副作用,boundary的值也将毫无意义,从而导致错误的结果或其他未定义的行为。
  • 测试数组边界很简单,比这种不完整的解决方法要简单得多。
  • 此方法需要分配和复制两个数组,而右半部分将不需要保存,因为boundary不会覆盖尚未复制的值。

我认为完全不应该教授这种方法。

这是没有这些缺点的另一种实现方式:

merge