СОРТИРОВКА ДАННЫХ
[Оглавление] [<< страница] [>>страница]


   6.2. Прямое слияние
 

Сортировка, называемая прямым слиянием, выполняется следующим образом.


Каждый проход (этап) сортировки включает две фазы:

Рис. Двухфазная сортировка прямым слиянием


Рассмотрим пример.
Пусть в исходном файле А ключи сортировки будут следующие:
А: 8 2 13 4 15 6 9 11 3 7 5 10 1 12 14
На первом этапе получаем:
В: 8 2 13 4 15 6 9 11
С: 3 7 5 10 1 12 14
А: 3 8 2 7 5 13 4 10 1 15 6 12 9 14 11
Второй этап:
В: 3 8 2 7 5 13 4 10
С: 1 15 6 12 9 14 11
А: 1 3 8 15 2 6 7 12 5 9 13 14 4 10 11
Третий этап:
В: 1 3 8 15 2 6 7 12
С: 5 9 13 14 4 10 11
А: 1 3 5 8 9 13 14 15 2 4 6 7 10 11 12
Четвертый этап:
В: 1 3 5 8 9 13 14 15
С: 2 4 6 7 10 11 12
А: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Теперь рассмотрим более детально, какие операции выполняются при каждой фазе при двухфазной сортировке. Фаза разделения:

Фаза слияния:

Сортировка завершается тогда, когда длина порции достигнет n. Приведем пример двухфазной сортировки прямым слиянием, записи файла — целые числа (ключи).
Пример.
/* Сортировка файла прямым слиянием, двухфазная сортировка */
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main()
{int vsort1 (char* r);
int n,b; char f[40]="A";
FILE *fin,*fout;
printf("\n Создание файла А");
if ((fout=fopen(f,"w"))== NULL)
{ printf("\n Файл %s не создан",f);
getch(); return -1;
}
n=100;
printf("\n Исходный файл:\n");
while (n--)
{ b=rand();
/* заполняем файл случайными целыми числами */
fprintf(fout," %d",b);
printf("%d\t",b);
}
fclose(fout);
vsort1(f);                                               /* вызов функции внешней сортировки */
printf("\n Отсортированный файл:\n");
fin=fopen(f,"r");
while (fscanf(fin,"%d",&b) !=EOF)
printf("%d\t",b);
fclose(fin);
getch(); return 0;
}
/* ВНЕШНЯЯ ДВУХФАЗНАЯ СОРТИРОВКА ПРЯМЫМ СЛИЯНИЕМ, фаза
разделения и вызов фазы слияния */
int vsort1 (char *ff)
/* ff — исходный файл, подлежащий сортировке */
{int vsort2(char *a,int m);                                     /* функция фазы слияния */
FILE *A,*B,*C;                                                   /* файловые переменные */
int a;                                                     /* для чтения из исходного файла */
int nb,nc;                                                     /* счетчики элементов в формируемых группах */
int р;                                                     /* р=1 — признак достижения конца исходного файла */
int m=1;                                                     /* число элементов в группе: 1,2,4,8,...*/
int k=0;                                                    /"счетчик числа элементов в исходном файле */
while(1)
{if ((A=fopen(ff,"r"))== NULL)
{ printf("\n Файл %s не открывается",ff);
getch(); return -1; }
if ((B=fopen("B","w"))== NULL)
{ printf("\n Файл В1 не открывается");
getch(); return -1;
}
if ((C=fopen("C","w"))== NULL)
{ printf("\n Файл С1 не открывается");
getch(); return -1;
}
nb=0; nc=0; p=0;
while (1)
{while (1)
{ if(fscanf(A,"%d",&a)== EOF)
             { p=1; break;}
             else
             { if (nb<m)                             /* формируем группу для файла В */
                { fprintf(B," %d ",a);
                k++; nb++; continue;}
               else
                 {fprintf(C," %d ",a);
                  nb=0; nc++; break;
               }
             }
}
if(p)
break;
while (1)
{ if(fscanf(A,"%d",&a)== EOF)
       { p=1;break;
}
else
     { if (nc           {fprintf(C,”%d”,a); k++;
             nc++; continue;
              }
              else
                  {fprintf(B,”%d”,a); k++;
                       nc=0; nb++;
                       break;
                    }
          }
}
if (p)
   break;
}
fclose(A); fclose(B); fclose(C);
vsort2(ff,m);          /* Вызов функции слияния */
if (m>=(k-k/2))          /* конец сортировки */
{     remove (“B”); remove (“C”);
      return 0;
}
m=m*2; k=0;
}
}
/* Фаза слияния */


vsort2(char *a,int m)
/* a - файл для слияния,
          m - число элементов в группах сливаемых файлов В и С */
{ FILE *A,*B,*C; /* файловые переменные */
     int b,c; /* для считывания данных из файлов В и С */
     int nb,nc;/*счетчики считанных из групп элементов файлов В и С*/
     int rb,rc; /* коды завершения операции считывания из файлов В и С*/
if ((A=fopen(a,"w")) == NULL)
     { printf("\n Файл %s не открывается",a);
     getch(); return -1;
     }
if ((B=fopen("B","r")) == NULL)
     { printf("\n Файл B не открывается");
     getch(); return -1;
     }
if ((C=fopen("C","r")) == NULL)
     { printf("\n Файл C не открывается");
      getch(); return -1;
     }
rb= fscanf(B,"%d",&b); rc=fscanf(C,"%d",&c);
nb=1;nc=1;
while(1)
    { if ((rb> 0) && (rc <= 0)) /* конец файла С */
                { fprintf(A," %d ",b);
                while (fscanf(B,"%d",&b) >0)
                fprintf(A," %d ",b);
                     fclose(A); fclose(B); fclose(C);
                return 0;
                }
           else
           { if ((rb ≤ 0) && (rc > 0)) /* конец файла В */
                    { fprintf(A," %d ",c);
               while (fscanf(C,"%d",&c)>0)
                    fprintf(A," %d ",c);
               fclose(A); fclose(B); fclose(C);
                    return 0;
               }
           else
                     if ((rb <= 0) && (rc <= 0))
           /* конец обоих файлов В и С, случается, если они оба пустые */
                     { fclose(A); fclose(B); fclose(C);
                          return 0;
                     }
                }
           if (nb≤m && nc≤m)
               { if (b≤c)
                     { fprintf(A," %d ",b);
                         rb=fscanf(B,"%d",&b);
                         nb++;
                         continue;
                     }
               else
                    { fprintf(A," %d ",c);
                         rc=fscanf(C,"%d",&c);
                    nc++;
                    continue;
                    }
               }
          if (nb≤m && nc>m)
               { fprintf(A," %d ",b);
               rb=fscanf(B,"%d",&b);
               nb++;
               continue;
               }
           if (nb>m && nc≤m)
                    { fprintf(A," %d ",c);
                    rc=fscanf(C,"%d",&c);
                    nc++;
                    continue;
                    }
                if (nb>m && nc>m)
                    { nb=1; nc=1;
                    continue;
               }
   }
}

Для выполнения рассмотренной двухфазной сортировки прямым слиянием нужны 3 файла (3 ленты), поэтому иногда ее называют трехленточным слиянием. Фаза разделения является вспомогательной, в ней записи не переставляются, но она занимает половину всех операций по пересылке записей (чтение из файла А и запись в файлы В и С). Если объединить разделение со слиянием, то от лишних операций можно избавиться. Теперь вместо слияния в одну последовательность результаты слияния сразу же распределяются по двум файлам, которые станут исходными для следующего прохода. Такая сортировка называется однофазной, но она требует наличия 4 файлов, по 2 входных и по 2 выходных при каждом проходе. С учетом этого такую сортировку называют также двухпутевой (балансированной сортировкой. Общее число пересылок при однофазной сортировке М = H*log2«. Число сравнений особого практического значения не имеет, так как затраты на операции пересылок (обмен данными с файлом) намного превышают затраты на сравнение. Рассмотрим пример.
/* ******************************************************* */
/* Сортировка файла прямым слиянием, однофазная сортировка */
/* фаза слияния-разделения выполняется нерекурсивной функцией */
/* 3 вспомогательных файла \vsort21.cpp */
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
/* ======================================================= */
main()
{int vsort1(char* r);
int n,b; char f[40]="A";
FILE *fin,*fout;
printf("\n Создание файла А");
if ((fout=fopen(f,"w")) == NULL)
     { printf("\n Файл %s не создан",f);
     getch(); return -1;
     }
n=1024;
printf("\n Исходный файл:\n");
while (n--)
     { b=rand(); /* заполняем файл случайными целыми числами */
     fprintf(fout," %d",b);
      printf("%d\t",b);
     }
fclose(fout);
vsort1(f); /* вызов функции внешней сортировки */
printf("\n Отсортированный файл:\n");
fin=fopen(f,"r");
while (fscanf(fin,"%d",&b) != EOF)
     printf("%d\t",b);
fclose(fin);
getch(); return 0;
}
/* ===================================================== */
/* ВНЕШНЯЯ ОДНОФАЗНАЯ СОРТИРОВКА ПРЯМЫМ СЛИЯНИЕМ,
     фаза начального разделения и вызов фазы слияния-разделения */
int vsort1(char *ff)
/* ff - исходный файл, подлежащий сортировке */
{int vsort2(char *f,int k); /*функция фазы слияния-разделения*/
FILE *A,*B,*C; /* файловые переменные */
     /* файлы "B", "C", "E" в функциях - временные */
int a; /* для чтения из исходного файла */
int nb,nc; /* счетчики элементов в формируемых группах */
int p; /* p=1 - признак достижения конца исходного файла */
     int m=1; /* число элементов в группе: 1,2,4,8,...*/
     int k=0; /*счетчик числа элементов в исходном файле */
while(1)
{ if ((A=fopen(ff,"r")) == NULL)
     { printf("\n Файл %s не открывается",ff);
     getch(); return -1;
     }

if ((B=fopen("B","w")) == NULL)
     { printf("\n Файл B не открывается");
     getch(); return -1;
     }
if ((C=fopen("C","w")) == NULL)
     { printf("\n Файл C не открывается");
      getch(); return -1;
     }
nb=0; nc=0; p=0;
while (1)
    {while (1)
     { if(fscanf(A,"%d",&a) == EOF)
          { p=1; break;
          }
     else
           { if (nb<m) /* формируем группу для файла В */
               { fprintf(B," %d ",a);k++;
                     nb++; continue;
               }
                    else
                    { fprintf(C," %d ",a); k++;
                    nb=0; nc++;
                    break;
               }
          }
     }
     if (p)
     break;
while (1)
     { if(fscanf(A,"%d",&a) == EOF)
          { p=1;break;
           }
     else
          { if (nc<m) /* формируем группу для файла С */
          { fprintf(C," %d ",a); k++;
               nc++; continue;
                }
               else
               {fprintf(B," %d ",a); k++;
                    nc=0;nb++;
                    break;
               }
          }
     }
     if (p)
      break;
}
     fclose(A); fclose(B); fclose(C);
     vsort2(ff,k); /* вызов функции слияния */
     return 0;
m=m*2; k=0;
}
}
/* ====================================================== */
/* ВНЕШНЯЯ ОДНОФАЗНАЯ СОРТИРОВКА ПРЯМЫМ СЛИЯНИЕМ,
     фаза слияния-разделения. Функция нерекурсивная */
vsort2(char *f,int k)
{ FILE *B,*C,*D,*E; /* файловые переменные */
     int b,c; /* для считывания данных из файлов В и С */
     int nb,nc;/*счетчики считанных из групп элементов файлов В и С*/
     int nd,ne; /* счетчики сливаемых в группы элементов */
     int rb,rc; /* коды завершения операции считывания из файлов В и С*/
     int sm; /* число элементов в сливаемой группе: sm=2*m */
     static m=1; /* число элементов в исходной группе */
     static int p=1; /* переключатель */
while(1)
{if(p%2)
{
if ((B=fopen("B","r")) == NULL)
     { printf("\n Файл B не открывается");
     getch(); return -1;
     }
if ((C=fopen("C","r")) == NULL)
     { printf("\n Файл C не открывается");
     getch(); return -1;
     }
if ((D=fopen(f,"w")) == NULL)
     { printf("\n Файл D не открывается");
      getch(); return -1;
     }
if ((E=fopen("E","w")) == NULL)
     { printf("\n Файл E не открывается");
     getch(); return -1;
     }
}
else
{
if ((B=fopen(f,"r")) == NULL)
     { printf("\n Файл D не открывается");
     getch(); return -1;
     }
if ((C=fopen("E","r")) == NULL)
     { printf("\n Файл E не открывается");
     getch(); return -1;
     }
if ((D=fopen("B","w")) == NULL)
     { printf("\n Файл B не открывается");
     getch(); return -1;
     }
if ((E=fopen("C","w")) == NULL)
     { printf("\n Файл C не открывается");
     getch(); return -1;
     }
}
sm=2*m;
rb= fscanf(B,"%d",&b); rc=fscanf(C,"%d",&c);
nb=1;nc=1; nd=0;ne=0;
while(1)
{ if ((rb> 0) && (rc ≤ 0)) /* конец файла С */
          {if (nd<sm) /* формируем группу для файла D */
               { fprintf(D," %d ",b);
                nd++;
               }
           else
          { fprintf(E," %d ",b);
           ne++;
          }
           while (fscanf(B,"%d",&b) >0)
                { if (nd<sm) /* формируем группу для файла D */
               { fprintf(D," %d ",b);
                     nd++;
                }
               else
               { fprintf(E," %d ",b);
                          ne++;
                     }
                }
               fclose(B); fclose(C);
           break;
          }
          else
          { if ((rb ≤ 0) && (rc > 0)) /* конец файла В */
               { if (nd<sm) /* формируем группу для файла D */
               { fprintf(D," %d ",c);
                nd++;
               }
           else
               { fprintf(E," %d ",c);
                ne++;
               }
          while (fscanf(C,"%d",&c)>0)
               { if (nd<sm) /* формируем группу для файла
D */
                          { fprintf(D," %d ",c);
                         nd++;
                     }
                    else
                     { fprintf(E," %d ",c);
                    ne++;
                }
               }
               fclose(B); fclose(C);
          break;
          }
     else
               if ((rb ≤ 0) && (rc ≤ 0)) /* конец обоих файлов В и С,

               случается, если они оба пустые */
               { fclose(B); fclose(C);
                    break;
               }
          }
      if (nb≤m && nc≤m)
               { if (b≤c)
               { if (nd==sm && ne==sm)
                    { nd=0; ne=0;
                     }
                    if (nd<sm) /* формируем группу для файла D */
                    { fprintf(D," %d ",b);
                          nd++;
                     }
                    else
                    { fprintf(E," %d ",b);
                          ne++;
                     }
                    rb=fscanf(B,"%d",&b);
                    nb++;
                    continue;
                }
           else
               { if (nd==sm && ne==sm)
                    { nd=0; ne=0;
                     }
                    if (nd                    { fprintf(D," %d ",c);
                          nd++;
                     }
                    else
                    { fprintf(E," %d ",c);
                          ne++;
                    }
                    rc=fscanf(C,"%d",&c);
                    nc++;
                    continue;
                }
          }
      if (nb≤m && nc>m)
          { if (nd==sm && ne==sm)
               { nd=0; ne=0;
                }
                if (nd<sm) /* формируем группу для файла D */
                { fprintf(D," %d ",b);
                     nd++;
               }
                    else
                    { fprintf(E," %d ",b);
                          ne++;
                    }
                rb=fscanf(B,"%d",&b);
          nb++;
          continue;
          }
     if (nb>m && nc≤m)
          { if (nd==sm && ne==sm)
                { nd=0; ne=0;
                }
          if (nd<sm) /* формируем группу для файла D */
                { fprintf(D," %d ",c);
                     nd++;
                }
           else
                { fprintf(E," %d ",c);
                     ne++;
                }
          rc=fscanf(C,"%d",&c);
          nc++;
          continue;
          }
      if (nb>m && nc>m)
          { nb=1; nc=1;
           continue;
          }
} /* конец цикла */
          fclose(B); fclose(C); fclose(D); fclose(E);
if (m≥(k-k/2))
     { if (!(p%2))
          { remove(f);
                     rename("B",f);
          }
          remove("B"); remove("C");remove("E");
          return 0;
     }
      m=2*m;
      p++;
}
}

[Оглавление] [<<страница] [>>страница] [В начало ]