Следующая программа слияния, написанная в C++, работает без ошибок при вводе данных через консоль. Но когда я использую текстовые файлы для ввода, это дает мне ошибку сегментации. Я пробовал печатать сообщения в разных частях кода, чтобы проверить, где ошибка, но он ничего не печатает, кроме сообщения d seg-fault. Вот код. Он отлично работает, когда я не использую файлы для ввода/вывода вывода.
Я запустил эту прог в компиляторе gcc в виртуальном боксе 4.0.8 (ubuntu OS) с базовой памятью 1 ГБ. Cud ошибка из-за недостаточной памяти?
#include<iostream>
#include<fstream>
#include<sys/time.h>
using namespace std;
int array[50];
int barray[50];
void merge(int p,int q, int r)
{
int i,j,k,l;
l=p;
i=p;
j=q+1;
while((i<=q)&&(j<=r))
{
if(array[i]<=array[j])
barray[++k]=array[i++];
else
barray[k++]=array[j++];
}
if(i>q)
{
for(l=j;l<=r;l++)
{
barray[k]=array[l];
k++;
}
}
else
{
for(l=i;l<=q;l++)
{
barray[k]=array[l];
k++;
}
}
for(i=p;i<=r;i++)
array[i]=barray[i];
}
void mergesort(int p, int r)
{
int q;
if(p<r)
q=(p+r)/2;
mergesort(p,q);
mergesort(q+1,r);
merge(p,q,r);
}
int main()
{
int r, p, i;
struct timeval tv1, tv2;/* For finding the running time */
gettimeofday(&tv1, NULL);
ifstream fin;
ofstream fout;
fin.open("abc5.txt");
fout.open("new5.txt");
fin>>r;
while(fin)
{
for(i=1;i<=r;++i)
{
fin>>array[i];
}
}
mergesort(1,r);
for(i=1;i<=r;++i)
{
fout<<array[i]<<"\n";
}
gettimeofday(&tv2, NULL);
fout<<"Running Time: "<<(double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec)<<" sec";
fin.close();
fout.close();
return 0;
}
ВХОДНЫЙ ФАЙЛ 8 3 6 2 8 9 1 4 10
Ваша индексация оставляет желать лучшего. Я не буду стесняться рассказывать вам об этом, и не просто сказать, что я не собираюсь так сортировать. Но это то, что есть, поэтому я покрою его.
Важно понимать, что сортировка последовательностей с использованием алгоритмов разделения и захвата - это передача некоторой базовой ссылки в последовательности и некоторых смещений из этой ссылки. Алгоритм должен быть независим от внешних переменных (например, array[]
и barray[]
) и принимать их в качестве параметров. Кроме того, помните, что вы программируете в C++ (или C). Часть красоты обоих языков - их собственная способность указателя-арифметики. Он может быть прекрасно использован для таких алгоритмов, как merge-sort. После того, как я продемонстрирую, как работают функции слияния и слияния, я продемонстрирую, о чем я говорю, тогда я предложат самый тривиальный сорт сортировки, который вы можете сделать в C++, если вы используете стандартные функции библиотек.
Ваш первый код
Сначала переверните свою функцию, используя ваши параметры. Я взял на себя смелость переименовать параметры, чтобы они действительно значимы для рецензента. Алгоритм должен быть самоочевидным, и я настоятельно рекомендую сравнить его рядом с тем, что вы делали.
// low = starting index
// mid = middle index
// high = last index
void merge(int low, int mid, int high)
{
int i=low, j=mid, k=0;
while (i < mid && j <=high)
{
if (array[i] < array[j])
barray[k++] = array[i++];
else
barray[k++] = array[j++];
}
// finish whichever segment isn't done
while (i < mid)
barray[k++] = array[i++];
while (j <= high)
barray[k++] = array[j++];
// copy back
for (i=0;i<k;++i)
array[low+i] = barray[i];
}
void mergesort(int low, int high)
{
int len = high - low;
if (len < 2)
return;
int mid = low + len/2;
mergesort(low, mid);
mergesort(mid, high);
merge(low, mid, high);
}
Следует отметить, что этот алгоритм - это начальные переданные параметры, которые должны быть действительными индексами в последовательности. Другими словами, если вы читаете массив из 8 элементов, то действительные индексы равны 0..7
и поэтому вы будете использовать его как mergesort(0,7)
.
Другой подход
Традиционная сортировка слияния в C/C++ использует базовый индекс массива и длину в качестве его единственных параметров. Средняя точка вычисляется по алгоритму сортировки. Хотя это не является строго необходимым для алгоритма слияния (он тоже может просто использовать длину /2), он делает более надежный алгоритм для того, чтобы вы никогда не нуждались в слиянии с сегментом, который сейчас не разделяется на середину.
Не зацикливайтесь на использовании std :: vector, std :: random_device и т.д. Ниже. Они используются в функции main()
исключительно для заполнения массива случайных чисел, которые затем сортируются, отображаются как до, так и после операции сортировки. То, что я хочу, чтобы вы выбрались из этого, - это сам алгоритм.
#include <iostream>
#include <vector>
#include <random>
void merge(int ar[], std::size_t mid, std::size_t len)
{
if (len < 2)
return;
// temporary storage. normally I use a RAII container
// such as std::vector<>, but I wanted to demonstrate
// the algorithm and indexing, not the memory management.
// none the less it is worth noting.
int *tmp = new int[len];
std::size_t i=0, j=mid, k=0;
while (i < mid && j < len)
{
if (ar[i] < ar[j])
tmp[k++] = ar[i++];
else
tmp[k++] = ar[j++];
}
// complete the unfinished segment
while (i < mid)
tmp[k++] = ar[i++];
while (j < len)
tmp[k++] = ar[j++];
// and move back to the original array
for (i=0; i<len; ++i)
ar[i] = tmp[i];
delete [] tmp;
}
void mergesort(int ar[], std::size_t len)
{
if (len < 2)
return;
// note pointer arithemtic in second call.
mergesort(ar, len/2);
mergesort(ar+len/2, len - len/2);
merge(ar, len/2, len);
}
int main()
{
std::vector<int> data;
data.reserve(20);
std::random_device rd;
std::default_random_engine rng(rd());
std::uniform_int_distribution<> dist(1,50);
// populate array
std::generate_n(std::back_inserter(data),
data.capacity(),
[&](){ return dist(rng);});
// show on-screen
for (auto n : data)
std::cout << n << ' ';
std::cout << '\n';
// mergesort
mergesort(data.data(), data.size());
// show on-screen
for (auto n : data)
std::cout << n << ' ';
std::cout << '\n';
return 0;
}
Выход (меняется)
15 10 8 38 20 21 9 43 8 22 19 45 12 16 17 36 2 32 6 37
2 6 8 8 9 10 12 15 16 17 19 20 21 22 32 36 37 38 43 45
Часть, которую вы ненавидите
Пройдя всю эту работу, вы не будете так счастливы знать, что в стандартной библиотеке уже присутствуют два алгоритма, которые std::inplace_merge
, а именно std::merge
и std::inplace_merge
. Использование одного из них делает реализацию mergesort
тривиальным, как вы увидите ниже:
#include <algorithm>
void mergesort(int ar[], std::size_t len)
{
if (len < 2)
return;
mergesort(ar, len/2);
mergesort(ar+len/2, len-len/2);
std::inplace_merge(ar, ar+len/2, ar+len);
}
Помните, что если вам когда-либо понадобится использовать что-то, кроме std::sort
, которое честно ставит все это в точку почти бесполезности, помимо выполнения академических целей.
Поэтому, учитывая ваш ввод, похоже, что когда p = 1 и r = 1 вы используете неинициализированное значение для q в последующих вызовах mergesort.
Ошибка, которую вы получаете, связана с неограниченной рекурсией в функции mergesort.
k
перед его использованием, как в index для разыменования в вашем алгоритме слияния. Одно это вызовет неопределенное поведение . Кроме того, проверьте ваш приростk
в двух основных назначениях. Это не соответствует, и это должно быть. Честно говоря, алгоритм слияния просто требует доработки.