Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Сортировка.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
155.94 Кб
Скачать
    1. Внешние сортировки

Основным понятием при использовании внешней сортировки является понятие серии. Серия (упорядоченный отрезок) – это последовательность элементов, которая упорядочена по ключу. Количество элементов в серии называется длиной серии. Серия, состоящая из одного элемента, упорядочена всегда. Последняя серия может иметь длину меньшую, чем остальные серии файлов. Максимальное количество серий в файле N (все элементы не упорядочены). Минимальное количество серий одна (все элементы упорядочены). В основе большинства методов внешних сортировок лежит процедура слияния и процедура распределенияСлияние – это процесс объединения двух (или более) упорядоченных серий в одну упорядоченную последовательность при помощи циклического выбора элементов, доступных в данный момент. Распределение – это процесс разделения упорядоченных серий на два и несколько вспомогательных файла. Фаза – это действия по однократной обработке всей последовательности элементов. Двухфазная сортировка – это сортировка, в которой отдельно реализуется две фазы: распределение и слияние. Однофазная сортировка – это сортировка, в которой объединены фазы распределения и слияния в одну. Двухпутевым слиянием называется сортировка, в которой данные распределяются на два вспомогательных файла. Многопутевым слиянием называется сортировка, в которой данные распределяются на N (N > 2) вспомогательных файлов.

      1. Прямым слиянием

Одна из сортировок на основе слияния называется простым слиянием.

Алгоритм сортировки простым слияния является простейшим алгоритмом внешней сортировки, основанный на процедуре слияния серией.

В данном алгоритме длина серий фиксируется на каждом шаге. В исходном файле все серии имеют длину 1, после первого шага она равна 2, после второго – 4, после третьего – 8, после k -го шага – 2k.

Алгоритм сортировки простым слиянием

Шаг 1. Исходный файл f разбивается на два вспомогательных файла f1 и f2.

Шаг 2. Вспомогательные файлы f1 и f2 сливаются в файл f, при этом одиночные элементы образуют упорядоченные пары.

Шаг 3. Полученный файл f вновь обрабатывается, как указано в шагах 1 и 2. При этом упорядоченные пары переходят в упорядоченные четверки.

Шаг 4. Повторяя шаги, сливаем четверки в восьмерки и т.д., каждый раз удваивая длину слитых последовательностей до тех пор, пока не будет упорядочен целиком весь файл ( рис. 43.1).

После выполнения i проходов получаем два файла, состоящих из серий длины 2i. Окончание процесса происходит при выполнении условия 2i>=n. Следовательно, процесс сортировки простым слиянием требует порядка O(log n) проходов по данным.

Признаками конца сортировки простым слиянием являются следующие условия:

  • длина серии не меньше количества элементов в файле (определяется после фазы слияния);

  • количество серий равно 1 (определяется на фазе слияния).

  • при однофазной сортировке второй по счету вспомогательный файл после распределения серий остался пустым.

Реализаия в файле sort.h

#pragma once

#include <fstream>

#include <iostream>

#include <string>

using std::fstream;

using std::string;

using std::ios;

using std::cout;

using std::endl;

template<class T>

class DOSort{

fstream file, file1, file2;

//Имя файла

string fileName;

//Кол-во

int amountObj;

//Длина серии

int srLen;

public:

//Конструкторы

DOSort(): amountObj(0), srLen(0), fileName("") {}

DOSort(string fName) {reset(fName);}

///////////////////////////////////

//Назначить новый файл(сброс)

void reset(string fName);

//Выполнить сортировку (точка входа)

void run();

private:

//Подсчёт значений

void countObj();

//Разделение на файлы

void splitFile();

//Объединение в файл

void mergeFiles();

///////////////////////////////////

//Базовая сортировка в mergeFiles()

void basicSort(T &obj1, T &obj2);

///////////////////////////////////

//Ошибка

void error() {exit(0);}

//Закрытие файлов

void closeFiles();

//Удаление временных файлов

void removeTmpFiles();

};

///////////////////////////////////

// Реализация

///////////////////////////////////

template<class T>

void DOSort<T>::reset(string fName)

{

fileName = fName;

amountObj = 0;

srLen = 1;

}

///////////////////////////////////

template<class T>

void DOSort<T>::run()

{

//Счёт объектов в файле

countObj();

//Выполнять пока длина серии меньше кол-во объектов

while(srLen < amountObj){

//Разделение на 2 файла

splitFile();

//Объединение в файл

mergeFiles();

//Увеличение длины серии

srLen*=2;

}

//Удаление временных файлов

removeTmpFiles();

}

///////////////////////////////////

template<class T>

void DOSort<T>::countObj()

{

//Для перебора элементов

T temp;

//Открытие файла

file.open(fileName, ios::in);

if (!file.is_open())

error();

//Подсчёт элементов

while(!file.eof()){

file >> temp;

amountObj++;

}

//Закрытие исх файла

file.close();

}

///////////////////////////////////

template<class T>

void DOSort<T>::splitFile()

{

//Открытие файлов

file.open(fileName, ios::in);

file1.open(fileName + ".part1", ios::out);

file2.open(fileName + ".part2", ios::out);

//Проверка на открытие файлов

if (!file || !file1 || !file2)

error();

//Объект для копирования

T obj;

//Пока не конец файла

while(!file.eof()){

//Файл 1

for(int i = 0; i < srLen && !file.eof(); i++){

file >> obj;

if (file)

file1 << obj << " ";

}

//Файл 2

for(int i = 0; i < srLen && !file.eof(); i++){

file >> obj;

if (file)

file2 << obj << " ";

}

}

//Закрытие файлов

closeFiles();

}

///////////////////////////////////

template<class T>

void DOSort<T>::mergeFiles()

{

//Открытие файлов

file.open(fileName, ios::out);

file1.open(fileName + ".part1", ios::in);

file2.open(fileName + ".part2", ios::in);

//Проверка на открытие файлов

if (!file || !file1 || !file2)

error();

//tmp объекты

T obj1, obj2;

//1-ое извлечение

file1 >> obj1;

file2 >> obj2;

//Пока не конец файла

while(!file1.eof() && !file2.eof())

basicSort(obj1, obj2);

//Копирование остатков file1 в file

while(!file1.eof()){

file << obj1 << " ";

file1 >> obj1;

}

//Копирование остатков file2 в file

while(!file2.eof()){

file << obj2 << " ";

file2 >> obj2;

}

//Закрытие файлов

closeFiles();

}

///////////////////////////////////

template<class T>

void DOSort<T>::basicSort(T &obj1, T &obj2)

{

int i = 0, j = 0;

//Распределение

while(i < srLen && j < srLen && !file1.eof() && !file2.eof()){

if (obj1 < obj2){

file << obj1 << " ";

file1 >> obj1;

i++;

}else{

file << obj2 << " ";

file2 >> obj2;

j++;

}

}

//Доп. копирование остатков fil1 в file

while (i < srLen && !file1.eof()){

file << obj1 << " ";

file1 >> obj1;

i++;

}

//Доп. копирование остатков file2 в file

while (j < srLen && !file2.eof()){

file << obj2 << " ";

file2 >> obj2;

j++;

}

}

///////////////////////////////////

template<class T>

void DOSort<T>::closeFiles()

{

file.close();

file1.close();

file2.close();

}

///////////////////////////////////

template<class T>

void DOSort<T>::removeTmpFiles()

{

remove(string(fileName + ".part1").c_str());

remove(string(fileName + ".part2").c_str());

}