CRLS понимает код границы сортировки слияния в коде C - PullRequest
1 голос
/ 04 апреля 2019
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 голосов
/ 06 апреля 2019

В коде используется общий подход для mergesort, когда копии делаются из обоих подмассивов с дополнительным элементом в конце, значение которого больше максимального значения обоих массивов.

Оператор boundary = tmpL[n1 - 1] > tmpR[n2 - 1] ? tmpL[n1 - 1] + 1 : tmpR[n2 - 1] + 1; пытается вычислить значение boundary как 1 плюс максимальное значение tmpL или tmpR в зависимости от того, какое значение больше. Он использует троичное выражение, которое примерно эквивалентно записи:

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

Цикл объединения может затем использовать один тест k <= r, чтобы остановить цикл, и i будет равен n1, а j будет равно n2, когда k достигнет r + 1.

Этот подход во многих отношениях нарушен:

  • , если любое из подмассивов содержит максимальное значение INT_MAX, вычисление boundary будет переполнено и вызовет неопределенное поведение. Даже если переполнение не приведет к фатальному побочному эффекту, значение boundary будет бессмысленным, что приведет к неверным результатам или другому неопределенному поведению.
  • Тестирование границ массива просто, намного проще, чем этот неполный обходной путь.
  • этот метод требует выделения и копирования обоих массивов, тогда как правая половина не требует сохранения, поскольку merge не будет перезаписывать значения, которые еще не были скопированы.

На мой взгляд, этому методу вообще не следует учить.

Вот альтернативная реализация без этих недостатков:

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

    // It is much simpler to consider q to point to the first element of
    // the second subarray and r to point after the last element of that array.
    q++;
    r++;

    n1 = q - p;  // number of elements in the left sorted subarray
    n2 = r - q;  // number of elements in the right sorted subarray

    tmpL = (int *)malloc(sizeof(int) * n1);
    if (tmpL == NULL) {
        // Report this fatal error or fall back to a different 
        // algorithm that does not require allocation, such as
        // heapsort or insertion sort.
        return;
    }
    // Make a copy of the left subarray as elements may be overwritten in the loop.
    for (i = 0; i < n1; i++) {
        tmpL[i] = A[p + i];
    }

    // Merge the elements of the subarrays:
    // - if all elements of the left array have been merged, 
    //   the remaining elements of the right subarray are already in place
    // - if k has reached r, all elements have been sorted
    for (i = j = 0, k = p; i < n1 && k < r; k++) {
        if (j >= n2 || tmpL[i] <= A[q + j]) {
            // Take the element from tmpL if the right subarray is empty
            //    or if it is no greater than the next one from the right subarray.
            A[k] = tmpL[i];
            i++;
        } else {
            // Otherwise take the element from the right subarray.
            A[k] = a[q + j];
            j++;
        }
    }
    free(tmpL);
}
0 голосов
/ 04 апреля 2019

merge () должен объединить два уже отсортированных прогона в A, от A [p] до A [q] и от A [q + 1] до A [r] (включительно).Создаются TmpL и TmpR, каждый с пробелом для одного дополнительного элемента в конце, который будет использоваться в качестве значения часового, которое больше любого значения в 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 к максимальному значению приведет к наименьшему значению.

...