Я сталкиваюсь с проблемами при вызове рекурсивной функции в сортировке слиянием на языке C

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

Круговой сдвиг вправо

void rightCircularShift(int *a, int startPos, int endPos)
{
    int temp, j;
    temp = a[endPos - 1];
    for (j = endPos - 1; j > startPos; j--) {
        a[j] = a[j - 1];
    }
    a[startPos] = temp;
}

Объединить

void merge(int *a, int l, int r, int m)
{
    int leftCounter = l, elementsMoved = 0;
    int rightCounter = m;
    while (elementsMoved < r) {
        if (leftCounter != r || rightCounter != r) {
            if (a[leftCounter] < a[rightCounter]) {
                leftCounter++;
            } else {
                rightCircularShift(a, leftCounter, rightCounter + 1);
                leftCounter++;
                rightCounter++;
            }
        } else {
            break;
        }
        elementsMoved++;
    }
}

Основной

int main()
{
    int n, i, *array;
    system("clear");
    printf("\nEnter number of elements: ");
    scanf("%d", &n);
    array = (int *)malloc(sizeof(int) * n);
    printf("Enter value of elements:\n");
    for (i = 0; i < n; i++) {
        scanf("%d", &array[i]);
    }
    printf("\n");
    mergeSort(array, 0, n - 1);
    printf("\n");
    for (i = 0; i < n; i++) {
        printf("%d\t", array[i]);
    }
    printf("\n");
    return 0;
}

Сортировка слиянием

void mergeSort(int *a, int left, int right)
{
    int mid = (left + right + 1) / 2;
    if (left < right) { 
        mergeSort(a, left, mid);
        mergeSort(a, mid + 1, right);
        merge(a, left, right, mid);
    }   
}

Я убедился, что остальные функции, такие как циклический сдвиг и слияние, правильно работают с тестовыми примерами, но я получаю эту ошибку:

«gcc дает сегментацию» — компилятор seg-faults? Это нехорошо. Почти уверен, что это ошибка вашей программы, которую вы можете очень быстро отладить, запустив ее с помощью другого инструмента в вашем наборе инструментов: вашего отладчика (например, gdb). Это остановит вашу программу при ошибке и позволит вам подробно изучить переменные и состояние стека вызовов.

WhozCraig 18.02.2023 07:03

Вы прошли через это в отладчике, чтобы найти точное местонахождение проблемы?

Shawn 18.02.2023 07:03
if (leftCounter != r || rightCounter != r) Вы, наверное, хотите && туда.
user3386109 18.02.2023 07:29

Функция merge не имеет смысла. В частности, круговое смещение. Независимо от правильности, этот круговой сдвиг полностью разрушает производительность. Весь смысл сортировки слиянием в том, что она выполняется за время O(n*log(n)). Эта реализация не будет. Я понимаю, что круговой сдвиг позволяет выполнять слияние на месте, но за счет производительности, которая уничтожает преимущества выполнения сортировки слиянием. Простая пузырьковая сортировка O(n^2) была бы быстрее, чем эта.

Tom Karzes 18.02.2023 07:51
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел &quot;Заголовок&quot; в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
1
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В функции mergeSort есть проблема: вы неправильно вычисляете индекс mid, для среза из 2 элементов mid будет равно right, а вызов mergeSort(a, mid + 1, right) будет иметь неопределенное поведение, потому что mid + 1 > right — условие, которое функция не может обработать.

Вы должны использовать int mid = (left + right) / 2; или лучше:

    int mid = left + (right - left) / 2;  // prevent potential overflow

В функции merge тоже есть проблема: тест elementsMoved < r некорректен, так как r — это индекс последнего элемента, а elementsMoved количество итераций. Нет смысла повторять более r - l + 1 раз, но тест этого не реализует.

Вы используете 2 разных соглашения в своем коде:

  • endPos - это индекс после конца среза в void rightCircularShift(int *a, int startPos, int endPos)
  • right — индекс последнего элемента среза в void mergeSort(int *a, int left, int right)

Было бы менее запутанно всегда использовать первое соглашение, принятое в C, где индексы массива начинаются с 0.

Кроме того, вы не должны использовать <= в if (a[leftCounter] <= a[rightCounter]), чтобы сделать сортировку стабильной.

Вот модифицированная версия:

#include <stdio.h>
#include <stdlib.h>

void rightCircularShift(int *a, int startPos, int endPos) {
    if (startPos < endPos) {
        int temp = a[endPos - 1];
        for (int j = endPos - 1; j > startPos; j--) {
            a[j] = a[j - 1];
        }
        a[startPos] = temp;
    }
}

void merge(int *a, int left, int mid, int right) {
    int leftCounter = left;
    int rightCounter = mid;
    while (leftCounter < mid && rightCounter < right) {
        if (a[leftCounter] <= a[rightCounter]) {
            leftCounter++;
        } else {
            rightCircularShift(a, leftCounter, rightCounter + 1);
            leftCounter++;
            rightCounter++;
            mid++;
        }
    }
}

void mergeSort(int *a, int left, int right) {
    if (right - left > 1) {
        // mid is the index of the first element of the second slice
        int mid = left + (right - left) / 2;
        mergeSort(a, left, mid);
        mergeSort(a, mid, right);
        merge(a, left, mid, right);
    }
}

int main() {
    int n, i, *array;
    system("clear");
    printf("\nEnter number of elements: ");
    if (scanf("%d", &n) != 1 || n <= 0)
        return 1;
    array = malloc(sizeof(*array) * n);
    if (array == NULL)
        return 1;
    printf("Enter value of elements:\n");
    for (i = 0; i < n; i++) {
        if (scanf("%d", &array[i]) != 1)
            return 1;
    }
    printf("\n");
    mergeSort(array, 0, n);
    printf("\n");
    for (i = 0; i < n; i++) {
        printf("%d\t", array[i]);
    }
    printf("\n");
    free(array);
    return 0;
}

Однако обратите внимание, что реализация сортировки слиянием таким образом очень неэффективна: необходимое пространство ограничено O (log (N)), что соответствует глубине рекурсии, но средняя временная сложность увеличивается до O (N2) или хуже, особенно для очень проверенный пример: массив, отсортированный в порядке убывания.

Есть способы реализовать сортировку слиянием с ограниченным объемом памяти, ограничивая при этом этот недостаток, но они более сложны, чем этот простой подход.

Спасибо, я на самом деле знаю, что очень неэффективно реализовывать сортировку слиянием таким образом, что она имеет временную сложность O (n2logn), но мне сказали сделать это именно таким образом.

Aditya Kale 18.02.2023 15:13

Другие вопросы по теме