Сортировка слиянием, ввод / вывод с использованием файлов показывает ошибку сегментации

0

Следующая программа слияния, написанная в 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

  • 0
    Можно также опубликовать входной файл, так как он не должен превышать 50 строк. Если это так, то в сочетании с неправильным использованием массивов C / C ++ на основе 1 и 0 легко вызовет UB.
  • 2
    Примечание: вы никогда не инициализируете k перед его использованием, как в index для разыменования в вашем алгоритме слияния. Одно это вызовет неопределенное поведение . Кроме того, проверьте ваш прирост k в двух основных назначениях. Это не соответствует, и это должно быть. Честно говоря, алгоритм слияния просто требует доработки.
Показать ещё 1 комментарий
Теги:
segmentation-fault
mergesort

2 ответа

2
Лучший ответ

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

Важно понимать, что сортировка последовательностей с использованием алгоритмов разделения и захвата - это передача некоторой базовой ссылки в последовательности и некоторых смещений из этой ссылки. Алгоритм должен быть независим от внешних переменных (например, 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, которое честно ставит все это в точку почти бесполезности, помимо выполнения академических целей.

  • 0
    Вероятно, это связано с назначением класса, а не с его собственной реализацией.
  • 1
    @AlbertMyers Я согласен, и в таких случаях я не использую движок "просто используй std :: sort", как это часто делают многие другие. Это был смысл ответа из трех частей: (а) это то, что не так с вашим кодом, (б) это другой способ, и (в) это способ с наименьшим количеством работы. Когда-нибудь, я надеюсь, что «я не должен делать это, потому что это уже в свободе», комфорт вступит в силу.
Показать ещё 1 комментарий
0

Поэтому, учитывая ваш ввод, похоже, что когда p = 1 и r = 1 вы используете неинициализированное значение для q в последующих вызовах mergesort.

Ошибка, которую вы получаете, связана с неограниченной рекурсией в функции mergesort.

  • 0
    я пробовал только с 8 элементами, это тоже не работает.
  • 0
    Что @WhozCraig сказал ... вы можете опубликовать свой вклад?
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню