#arrays #c #algorithm #sorting #mergesort
#массивы #c #алгоритм #сортировка #сортировка слиянием
Вопрос:
Здравствуйте, я работаю над применением алгоритма сортировки слиянием, однако через несколько часов я не могу найти решение. Любая помощь или подсказка будут оценены. Я пытался отладить свой код, но после многих попыток мне не удалось увидеть, в чем ошибка.
Проблема в том, что алгоритм выдает неверный результат. Пример:
ввод: (10, 5, 16, 2)
вывод: (2, 5, 5, 10)
#include <stdio.h>
void mergesort(int A[], int l, int r);
void merge(int A[], int l, int q, int r);
int main() {
int n;
scanf("%d", amp;n);
int tablica[n];
for (int i = 0; i < n; i )
scanf("%d", amp;tablica[i]);
mergesort(tablica, 0, n - 1);
for (int i = 0; i < n; i )
printf("%d ", tablica[i]);
return 0;
}
void mergesort(int A[], int l, int r) {
if (l < r) {
int q = (l r) / 2;
mergesort(A, l, q);
mergesort(A, q 1, r);
merge(A, l, q, r);
}
}
void merge(int A[], int l, int q, int r) {
int n = q - l 1;
int m = r - q;
int B[n];
int C[m];
for (int i = 0; i < n; i )
B[i] = A[i];
for (int i = 0; i < m; i )
C[i] = A[q 1 i];
int i = 0;
int j = 0;
for (int k = l; k <= r; k ) {
if (B[i] <= C[j] || j >= m) {
A[k] = B[i];
i ;
} else {
A[k] = C[j];
j ;
}
}
}
Комментарии:
1. Создайте минимально возможный набор входных данных, который вызывает проблему, и жестко запрограммируйте его в своем приложении. Затем используйте отладчик для пошагового выполнения вашего кода оператор за оператором, отслеживая переменные и их значения. Это обычный способ решения проблем, подобных этой.
2. @DamianKowalski: не могли бы вы принять один из ответов, нажав на серую галочку под его оценкой.
Ответ №1:
Не зная, что именно не работает (не компилируется ли оно? Вы получаете неверный вывод для некоторых входных данных?) трудно помочь. Здесь как минимум одна ошибка:
if(B[i] <= C[j] || j >= m)
Должно быть
if(j >= m || i < n amp;amp; B[i] <= C[j])
Важно проверить j >= m
, прежде чем проверять неравенство, и добавить i < n
проверку.
Без последнего, как только вы полностью используете B
массив, B[i]
он выйдет за границы массива, и вы получите неопределенное поведение.
Без первого раза j >= m
условие B[i] <= C[j]
будет проверяться ранее j >= m
, также вызывая неопределенное поведение.
ОБНОВЛЕНИЕ: в приведенном вами фактическом примере вторая ошибка сводится к замене B[i] = A[i]
на B[i] = A[l i]
. С этими двумя изменениями приведенный вами пример работает.
Ответ №2:
Функция merge
имеет несколько ошибок.
Например, размер массива C должен вычисляться как
int m = r - q 1;
вместо
int m = r - q;
Вместо этого цикла for
for(int i = 0; i < n; i )
B[i] = A[i];
вы должны написать
for(int i = 0; i < n; i )
B[i] = A[l i];
Этот оператор if
if(B[i] <= C[j] || j >= m)
может привести к неопределенному поведению, поскольку нет проверки достоверности используемых индексов i и j, принадлежат ли они допустимым диапазонам.
Функции могут быть определены следующим образом, как показано в демонстрационной программе ниже.
#include <stdio.h>
void merge( int a[], size_t left, size_t middle, size_t right )
{
size_t n1 = middle - left;
size_t n2 = right - middle;
int a1[n1];
int a2[n2];
for ( size_t i = 0; i < n1; i )
{
a1[i] = a[left i];
}
for ( size_t i = 0; i < n2; i )
{
a2[i] = a[middle i];
}
size_t i = left, i1 = 0, i2 = 0;
while ( i1 < n1 amp;amp; i2 < n2 )
{
a[i ] = a2[i2] < a1[i1] ? a2[i2 ] : a1[i1 ];
}
while ( i1 < n1 ) a[i ] = a1[i1 ];
while ( i2 < n2 ) a[i ] = a2[i2 ];
}
void mergesort( int a[], size_t left, size_t right )
{
if ( left 1 < right )
{
size_t middle = ( left right ) / 2;
mergesort( a, left, middle );
mergesort( a, middle, right );
merge( a, left, middle, right );
}
}
int main(void)
{
size_t n;
if ( scanf( "%zu", amp;n ) == 1 amp;amp; n != 0 )
{
int a[n];
for ( size_t i = 0; i < n; i ) scanf( "%d", a i );
mergesort( a, 0, n );
for ( size_t i = 0; i < n; i )
{
printf( "%d ", a[i] );
}
putchar( 'n' );
}
return 0;
}
Если входные данные
4
10 5 16 2
тогда результат будет
2 5 10 16
Комментарии:
1. Незначительный
( left right ) / 2
может переполниться.left (right - left)/2
не работает.
Ответ №3:
В merge
функции есть ошибка:
if (B[i] <= C[j] || j >= m)
обращается B[i]
к и C[j]
перед тестированием, если i
и j
находятся ниже их соответствующих границ. Тест должен быть:
if (i < n amp;amp; (j >= m || B[i] <= C[j])) {
A[k] = B[i ];
} else {
A[k] = C[j ];
}
Обратите также внимание на эти замечания:
- вам не нужно сохранять вторую половину массива, потому что его элементы копируются до того, как они будут перезаписаны.
- in
void mergesort(int A[], int l, int r)
r
следует исключить, чтобы избежать путаницы и ошибок1
/-1
корректировок. - аналогично, в
void merge(int A[], int l, int q, int r)
,q
должно быть начало второй половины иr
индекс элемента после конца среза. l
выглядит до смешения похожим на1
Вот измененная версия:
#include <stdio.h>
void mergesort(int A[], int low, int high);
void merge(int A[], int low, int mid, int high);
int main() {
int n;
if (scanf("%d", amp;n) != 1 || n <= 0)
return 1;
int tablica[n];
for (int i = 0; i < n; i ) {
if (scanf("%d", amp;tablica[i]) != 1)
return 1;
}
mergesort(tablica, 0, n);
for (int i = 0; i < n; i )
printf("%d ", tablica[i]);
printf("n");
return 0;
}
void mergesort(int A[], int low, int high) {
if (high - low >= 2) {
int mid = low (high - low) / 2;
mergesort(A, low, mid);
mergesort(A, mid, high);
merge(A, low, mid, high);
}
}
void merge(int A[], int low, int mid, int high) {
int n = mid - low;
int B[n];
for (int i = 0; i < n; i )
B[i] = A[low i];
int i = 0;
int j = mid;
int k = low;
while (i < n) {
if (j >= high || B[i] <= A[j]) {
A[k ] = B[i ];
} else {
A[k ] = C[j ];
}
}
}