EDIT
Ваши изменения сделали мой оригинальный ответ (ниже) бессмысленным для других читателей :) О, хорошо ...
Прежде всего необходимо решить, будут ли ваши указатели begin
и end
определять диапазон [начало, конец) или [начало, конец]. Я предлагаю первый выбор, потому что он используется в библиотеке C ++. Если Вы согласны с этим, звоните
mergesort(arr,&arr[0],&arr[n]);
правильно. Вы должны изменить &arr[n]
на &arr[n-1]
, только если решите, что вы хотите, чтобы указатели begin
и end
определяли диапазон [начало, конец], который я предлагаю не делать.
На самом деле указателей begin
и end
достаточно для сортировки диапазона, и вашей функции mergesort
не нужен первый параметр.
Неверный расчет med
med=arr[arr_end-arr_begin]/2+arr_begin;
В предыдущей версии это было правильно (переменная называлась q)
Звонки ниже также неверны
mergesort(arr,arr_begin,med);
mergesort(arr,med+1,arr_end);
Первый вызов должен сортировать диапазон [arr_begin, med), а не [arr_begin, med] (*med
не принадлежит этому диапазону), поэтому второй вызов должен сортировать диапазон, начиная с med
.
Распределение arr1 правильное, благодаря тому, что разница end - begin
равна количеству элементов. В этом преимущество выбора диапазона [начало, конец) вместо [начало, конец]. Однако было бы неплохо, если бы Вы освободили выделенную память после использования.
Неверный вызов слияния, как и звонки на mergesort
выше. Второй диапазон начинается с med
, потому что med
указывает за первый диапазон, а не на его последний элемент.
Реализация merge
слишком сложна. Вам не нужно ничего менять. Вы просто берете элементы из обоих диапазонов и копируете их в место назначения. Те три цикла, которые начали мой оригинальный пост (ниже), достаточно, но обратите внимание на условия.
Я повторяю еще раз med
точек мимо первого диапазона и arr_end
точек мимо второго диапазона. Принимая это во внимание, следует ли вам использовать <=
или <
операторов?
Мне не нравится несоответствие в условиях i<=q
, j<=u
, i<q
, j<u
в следующем коде:
while(i<=q && j<=u){
if(*i<*j){
*k=*i;
i=i+1;
}
else{
*k=*j;
j=j+1;
}
k=k+1;
}
while(i<q){*k=*i;i++;k++;}
while(j<u){*k=*j;j++;k++;}
Вы называете свой слияние следующим образом:
mergesort(arr,&arr[0],&arr[n]);
, что означает, что u
является указателем, который указывает на точку после последнего элемента Вашего массива . Другими словами, вы, кажется, думаете о своих указателях, как об итераторах begin
и end
, которые определяют диапазон [начало, конец) - *begin
принадлежит диапазону, но *end
нет,
В определении mergesort
Вы пишете
int *q=((u-p)/2)+p;
mergesort(arr,p,q);
mergesort(arr,q+1,u);
, что не согласуется с предположениями выше. q
может быть равно p
, если u == p+1
.
mergesort(arr,p,q);
означает сортировку диапазона [p, q) (q
выходит за пределы диапазона)
mergesort(arr,q+1,u);
означает сортировку диапазона [q + 1, u) (u
выходит за пределы диапазона)
Если бы вы были последовательны в представлении диапазона с указателями, вы бы никогда не коснулись элемента *q
таким образом.
Представление о диапазоне как [начало, конец) вместо [начало, конец] (во втором случае *end
является частью диапазона) согласуется с тем, как итераторы используются в C ++, но это не обязательно. Вы можете использовать указатели для определения диапазона также вторым способом, но в этом случае вам нужно изменить вызов mergesort(arr,&arr[0],&arr[n]);
на mergesort(arr,&arr[0],&arr[n-1]);
. В обоих случаях Вы должны переосмыслить условия в коде, приведенном в начале .
Это домашнее задание, поэтому я не буду решать его для Вас, но здесь есть небольшой намек, который может помочь подумать об этом. Переопределите Ваш merge
, так что для слияния требуется 2 диапазона:
void merge(int * destination, int * r1_begin, int * r1_end, int * r2_begin, int * r2_end);
и подумайте, как его использовать. Позже вы можете упростить вещи. Вам не нужен параметр назначения, и вам не нужно копировать все объединенные элементы. Сначала вы можете скопировать только один диапазон, а затем слить его непосредственно в место назначения, извлекая элементы из копии первого диапазона и из второго диапазона.