Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование задач с применением структурированных данных. Лабораторный практикум.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆

ЛАБОРАТОРНАЯ РАБОТА № 5

МЕТОДЫ СОРТИРОВКИ И ИХ ПРОГРАММНАЯ РЕАЛИЗАЦИЯ

5.1. Общие рекомендации по реализации методов сортировки

Цель лабораторной работы - освоение методов сортировки и их про-
граммной реализации.
Одной из важнейших процедур обработки структурированной информа-
ции является сортировка данных.
Сортировкой называют процесс перегруппировки заданной последова­тельности (кортежа) объектов в некотором определенном порядке. Определен­ный порядок (например, упорядочение в алфавитном порядке, по возрастанию или убыванию количественных характеристик, по классам, типам и т.п.) в по­следовательности объектов необходим для удобства работы с этими объектами. В частности, одной из целей сортировки является облегчение последующего поиска элементов в отсортированном множестве. Под поиском подразумевается процесс нахождения в заданном множестве некоторого объекта, обладающего необходимыми свойствами или качествами.
Алгоритмы сортировки имеют широкое практическое применение в про­граммных системах. Таким образом, целью алгоритмов сортировки является упорядочение последовательности элементов данных.
Поиск элемента в последовательности отсортированных данных занимает время, пропорциональное логарифму количества элементов в последовательно­сти. Поиск элемента в последовательности не отсортированных данных зани­мает время, пропорциональное количеству элементов в последовательности, что значительно больше первого варианта.
Существует множество различных методов сортировки данных. Однако любой алгоритм сортировки можно разбить на три основные части:
– сравнение, определяющее упорядоченность пары элементов;
– перестановка, меняющая местами пару элементов;
– собственно сортирующий алгоритм, который осуществляет сравнение и
перестановку элементов данных до тех пор, пока все эти элементы не будут упорядочены.
Важнейшей характеристикой любого алгоритма сортировки является ско­рость его работы, которая определяется функциональной зависимостью средне­го времени сортировки последовательностей элементов данных, заданной длинны, от этой длинны. Время сортировки будет пропорционально количеству сравнений и перестановки элементов данных в процессе их сортировки.
Существует несколько алгоритмов сортировки.
Сортировка методом пузырька
Алгоритм пузырьковой сортировки состоит из повторяющихся проходов по сортируемому массиву. За каждый проход элементы последовательно срав-
71
ниваются попарно и, если порядок в паре неверный, выполняется обмен эле­ментов. Проходы по массиву повторяются N − 1 раз или до тех пор, пока на очередном проходе не окажется, что обмены больше не нужны, и это будет означать, что массив отсортирован. При каждом проходе алгоритма по внут­реннему циклу очередной наибольший элемент массива ставится на своё место в конце массива рядом с предыдущим «наибольшим элементом. При этом наименьший элемент перемещается на одну позицию к началу массива («всплывает» до нужной позиции, как пузырёк в воде). Отсюда и название ал­горитма.
Особенность данного алгоритма заключается в следующем: после перво­го завершения внутреннего цикла максимальный элемент массива всегда нахо­дится на N-ой позиции. При втором проходе, следующий по значению макси­мальный элемент находится на N – 1 месте. И так далее. Таким образом, на каждом следующем проходе число обрабатываемых элементов уменьшается на 1 и нет необходимости «обходить» весь массив от начала до конца каждый раз.
Так как массив из одного элемента не нуждается в сортировке, то для сортировки требуется делать не более N − 1 итераций внешнего цикла. Поэтому в некоторых реализациях внешний цикл всегда выполняется ровно N – 1 и не отслеживается, были или не были обмены на каждой итерации.
Введение индикатора (флажка F) действительно произошедших во внут­реннем цикле обменов уменьшает число лишних проходов в случаях с частично отсортированными массивами на входе. Перед каждым проходом по внутрен­нему циклу флажок сбрасывается в 0, а после действительно произошедшего обмена устанавливается в 1. Если после выхода из внутреннего цикла флажок равен 0, то обменов не было, то есть массив отсортирован и можно досрочно выйти из программы сортировки. Общий алгоритм пузырьковой сортировки, реализующий возрастающую сортировку, приведен ниже.
ЦИКЛ ДЛЯ J=0 ДО N-1
F=0
ЦИКЛ ДЛЯ I=0 ДО N-J-1
ЕСЛИ A[I] > A[I+1]
ТО ОБМЕН A[I], A[I+1] F=1
СЛЕДУЮЩЕЕ I
ЕСЛИ F=0 ТО ВЫХОД ИЗ ЦИКЛА
СЛЕДУЮЩЕЕ J
Сортировка методом поиска минимума (максимума)
В данной сортировке используется методика поиска минимума (если упо­рядочить надо по возрастанию) или максимума (если упорядочить надо по убы­ванию) среди элементов массива.
Основное действие алгоритма следующее: на i-м шаге итерации среди элементов массива a[i]…a[n] ищется минимальный (максимальный) элемент массива и меняется местами с a[i]. На следующем шаге i увеличивается на еди-
72
ницу, и процедура поиска повторяется. Всего надо сделать n-1 итераций, где n – число элементов массива.
Общий алгоритм данной сортировки можно представить следующим об­разом:
ЦИКЛ ДЛЯ I=0 ДО N-1
MIN = A[I]
ЦИКЛ ДЛЯ J=I+1 ДО N
ЕСЛИ MIN > A[J] ТО
MIN = A[J], ОБМЕН A[J], A[I]
СЛЕДУЮЩЕЕ J СЛЕДУЮЩЕЕ I
Сортировка методом вставки
Сортировка вставками – алгоритм сортировки, в котором элементы вход­ной последовательности просматриваются по одному, и каждый следующий просматриваемый элемент размещается в подходящее место среди ранее упо­рядоченных элементов. Перераспределение элементов может происходить как в рамках заданного набора данных, так и во вновь выделяемой памяти. Но на практике почти всегда используется первый подход. Наиболее понятно метод объясняется графически, как показано на рис. 3.
Рис. 3. Графическое представление сортировки методом вставки
Сортировка вставками наиболее эффективна, когда массив уже частично отсортирован или когда элементов массива не много. Если же элементов мень­ше 10, то данный алгоритм часто вообще является лучшим. Поэтому он часто используется в качестве вспомогательного алгоритма для сортировки последо­вательностей в более сложных алгоритмах.
Данный алгоритм сортировки можно представить в общем виде следую­щим образом:
73
ЦИКЛ ДЛЯ I=0 ДО N
KEY = A[I]
J = I - 1
ПОКА (J>=0 И A[J]>KEY)
A[J+1] = A[J]
J = J – 1
A[J+1] = KEY
КОНЕЦ ПОКА СЛЕДУЮЩЕЕ I
Сортировка методом Шелла
При сортировке методом Шелла сначала сравниваются и сортируются между собой значения, стоящие один от другого на некотором расстоянии А. После этого процедура повторяется для некоторых меньших значений А, а за­вершается сортировка Шелла упорядочиванием элементов при А=1 (то есть, например, с применением обычной сортировки вставками).
Эффективность сортировки Шелла в определённых случаях обеспечива­ется тем, что элементы «быстрее» встают на свои места (в простых методах сортировки, например, пузырьковой, каждая перестановка двух элементов уменьшает количество инверсий в списке максимум на 1, а при сортировке Шелла это число может быть больше).
Сортировка Шелла во многих случаях медленнее, чем другие сортировки Однако она имеет ряд преимуществ:
– отсутствие потребности в памяти под стек;
– отсутствие деградации при неудачных наборах данных.
Среднее время работы алгоритма зависит от длин промежутков А, на ко­торых будут находиться сортируемые элементы исходного массива ёмко­стью N на каждом шаге алгоритма. Существует несколько подходов к выбору этих значений. Каноническим является подход, предложенный Шеллом a1=N/2, a2=a1/2, …, aK=1.
В качестве вспомогательной сортировки методом Шелла можно исполь­зовать сортировку вставками.
Пример сортировки Шелла приведен ниже.
Пример. Рассмотрим следующий алгоритм сортировки массива a[0].. a[15].
74
Шаг 1. Вначале сортируют массив простыми вставками каждые 8 групп из 2-х элементов (a[0], a[8[), (a[1], a[9]), ... , (a[7], a[15]).
Шаг 2. Потом сортируют каждую из четырех групп по 4 элемента (a[0], a[4], a[8], a[12]), ..., (a[3], a[7], a[11], a[15]).
В нулевой группе будут элементы 4, 12, 13, 18, в первой - 3, 5, 8, 9 и т.п.
Шаг 3. Далее сортируют 2 группы по 8 элементов, начиная с (a[0], a[2], a[4], a[6], a[8], a[10], a[12], a[14]).
Шаг 4. В конце сортируют вставками все 16 элементов.
Очевидно, лишь последняя сортировка необходима, чтобы расположить все элементы по своим местам. Зачем используют остальные?
Все предыдущие сортировки продвигают элементы максимально близко к соответствующим позициям, так что в последней стадии число перемещений бу­дет весьма невелико. Ускорение процесса сортировки подтверждено многочис­ленными исследованиями и на практике оказывается довольно существенным.
Единственной характеристикой сортировки Шелла является приращение ­расстояние между сортируемыми элементами, в зависимости от прохода. В конце приращение всегда равно единице - метод завершается обычной сорти­ровкой вставками, но именно последовательность приращений определяет рост эффективности.
Использованный в примере набор ..., 8, 4, 2, 1 - неплохой выбор, особен­но, когда количество элементов - степень двойки.
75
Нахождение времени работы программы (или ее части)
Для того, чтобы найти время работы программы, можно воспользоваться функцией clock(). Прототип функции clock() находится в заголовочном файле <ctime>, который нужно подключить.
Функция clock() возвращает значение времени в миллисекундах (1 с = 1000 мс). Причём отсчёт времени начинается с момента запуска программы. Если надо измерить работу всей программы, то в конце программы, перед опе­ратором return 0 нужно запустить функцию clock(), которая покажет рабочее время. Для поиска времени работы фрагмента кода нужно найти разницу между конечным и начальным временем, как показано ниже.
#include <ctime>
unsigned int start_time = clock(); // начальное время
// здесь должен быть фрагмент кода, время выполнения которого нужно
измерить
unsigned int end_time = clock(); // конечное время
unsigned int search_time = (end_time - start_time); // искомое время в
миллисекундах (для секунд надо поделить на 1000)

5.2. Задания к лабораторной работе № 5

Задание
Разработать консольное приложение, которое выполняет задания 1- 4.
Задание 1. Консольное приложение выполняет следующие действия:
– автоматически заполняет входной файл набором сгенерированных це- лых чисел (количество – 10000 штук);
– считывает из сформированного входного файла (in.txt) последователь- ность целых чисел;
– преобразует считанную последовательность в массив целых значений;
– сортирует по возрастанию полученный массив методом пузырька (с за- сечкой времени выполнения);
– записывает полученную отсортированную последовательность в вы- ходной файл (out_bubble.txt);
Задание 2. Консольное приложение выполняет следующие действия:
– считывает из сформированного входного файла (in.txt) последователь- ность целых чисел;
– преобразует считанную последовательность в массив целых значений;
– сортирует по возрастанию полученный массив методом поиска мини- мума (с засечкой времени выполнения);
– записывает полученную отсортированную последовательность в вы- ходной файл (out_min.txt);
Задание 3. Консольное приложение выполняет следующие действия:
– считывает из сформированного входного файла (in.txt) последователь-
ность целых чисел;
76
– преобразует считанную последовательность в массив целых значений;
– сортирует по возрастанию полученный массив методом вставки (с за- сечкой времени выполнения);
– записывает полученную отсортированную последовательность в вы- ходной файл (out_ins.txt);
Задание 4. Консольное приложение выполняет следующие действия:
– считывает из сформированного входного файла (in.txt) последователь- ность целых чисел;
– преобразует считанную последовательность в массив целых значений;
– сортирует по возрастанию полученный массив сортировкой Шелла (с засечкой времени выполнения);
– записывает полученную отсортированную последовательность в вы- ходной файл (out_shell.txt);
– произвести сравнение времени, затраченного на разные алгоритмы сор- тировки.
Рекомендации:
– все методы сортировки должны быть реализованы в одной программе и сортировать одну и ту же последовательность (чтобы можно было корректно сравнить время выполнения);
– заполнение массива из файла и различные методы сортировки следует выполнить отдельными функциями;
– на выходе можно получить примерно следующее окно с результатами, приведенное на следующем рис. 4.
Рис. 4. Окно с результатами работы программы
Отчет
Отчет должен содержать титульный лист, вариант задания, текст про-
граммы с комментариями, скриншоты результатов работы программы.
77

ЛАБОРАТОРНАЯ РАБОТА № 6

ЛИНЕЙНЫЕ СПИСКИ

6.1. Общие рекомендации по работе с динамическими структурами
данных
Цель лабораторной работы - освоение технологий программирования с
применением линейных списков.
Если размер обрабатываемых данных невозможно определить заранее, то память для их хранения выделяют по мере необходимости отдельными блока­ми, связанными друг с другом с помощью указателей. Такой способ организа­ции данных называется динамическими структурами данных. Размер таких данных изменяется во время выполнения программы.
Динамические структуры данных в программах можно представить как линейные списки, стеки, очереди и бинарные деревья.
Самый простой способ связывания множества элементов – это формиро­вание данных в виде списка, в котором каждый элемент содержит ссылку на следующий. Такой список называют однонаправленным (односвязным). Если добавить в каждый элемент вторую ссылку – на предыдущий элемент, получит­ся двунаправленный список (двусвязный). Если последний элемент связать ука­зателем с первым, то получится кольцевой список.
Типовая структура двунаправленного линейного списка может быть за­писана так:
struct Node{
int d;
Node *next;
Node *prev;
};.
Каждый элемент списка содержит ключ, идентифицирующий этот эле­мент. Ключ обычно бывает либо целым числом, либо строкой и является ча­стью поля данных. В качестве ключа в процессе работы со списком могут вы­ступать разные части поля данных. Например, если создается линейный список из записей, содержащих фамилию, год рождения, стаж работы и пол, любая часть записи может выступать в качестве ключа: при упорядочивании списка по алфавиту ключом будет фамилия, а при поиске, к примеру, ветеранов труда ключом будет стаж. Ключи разных элементов списка могут совпадать.
Над списками можно выполнять следующие операции:
– начальное формирование списка (создание первого элемента);
– добавление элемента в конец списка;
– чтение элемента с заданным ключом;
– вставка элемента в заданное место списка (до или после элемента с за-
данным ключом);
78
– удаление элемента с заданным ключом;
– упорядочивание списка по ключу.

6.2. Пример программы работы с линейным списком

Пример. Составить программу, формирующую сведения об отправлении поездов. Каждый поезд описывается с помощью структуры, содержащей сле­дующие поля:
– номер поезда (ключ списка);
– пункт назначения;
– время отправления.
Данные хранятся в виде двунаправленного линейного списка.
Программа должна реализовывать следующие операции:
– формирование меню для выбора действия пользователем;
– ввод данных в список, отсортированный по ключу;
– вывод данных из списка в виде таблицы;
– поиск элемента в списке по различным критериям (номеру поезда, име- ни пункта назначения, времени отправления);
– удаление элемента из списка по заданному ключу.
Листинг программы приведен ниже.
#include <Windows.h>
#include <iostream>
#include <iomanip>
using namespace std;
//Структура для формирования списка
struct Train {
int train_num; //Номер поезда
char p_nazn[20]; //Имя пункта назначения
char v_otpr[10]; //Время отправления
Train* prev; //Указатель на предыдущий элемент
Train* next; //Указатель на последующий элемент
};
Train* pbeg; //Указатель на начало списка
Train* pend; //Указатель на конец списка
//Список функций программы
void menu(); //Функция прорисовки меню
//Функция формирования первого элемента списка
Train* first(int t_num, char* p_nazn, char* v_otpr);
//Функция вставки элемента в упорядоченный список
79
void add_sort(Train** pbeg, Train** pend, int t_num, char* p_nazn, char* v_otpr);
//Функция поиска элемента по ключу (номер поезда)
Train* find(Train* const pbeg, int t_num);
//Функция поиска элемента по имени пункта назначения
bool find_punkt(Train* const pbeg, char* p_nazn);
//Функция поиска элемента по времени отправления
bool find_time(Train* const pbeg, char* v_otpr);
//Функция удаления элемента из списка по заданному
// ключу
bool remove(Train** pbeg, Train** pend, int key);
void vvod(); //Функция ввода данных в список
void vivod(); //Функция вывода элементов списка
//на экран
void search(); //Функция поиска элементов в списке
void del(); //Функция удаления элемента
// из списка
void hline(); //Функция прорисовки
//горизонтальной линии
int main()
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
pbeg = 0; //Инициализация указателя на первый элемент списка
pend = 0; //Инициализация указателя на последний элемент списка
int num = 0; //Переменная для хранения номера выбранного пункта меню
//Цикл обработки действий пользователя
while (num != 5)
{
menu();
cin >> num;
switch (num) {
case 1: vvod(); break;
case 2: vivod(); break;
case 3: search(); break;
case 4: del(); break;
case 5: break;
default: cout << "Неверный вариант выбора";
}
}
return 0;
}
80
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]