I = 1

advertisement
УЧЕБНО-МЕТОДИЧЕСКИЙ КОМПЛЕКС
ДИСЦИПЛИНЫ
«Методы сортировок и поиска»
для специальности 5В070400«Вычислительная техника и программное обеспечение»
Семей
2014
СОДЕРЖАНИЕ
1 ГЛОССАРИЙ
2 ЛЕКЦИИ
3 ПРАКТИЧЕСКИЕ ЗАНЯТИЯ
4 СРС И СРСП
1 ГЛОССАРИЙ
1.1 Сортировка – это процесс расстановки элементов «в некотором
порядке». Элементы размещаются так, чтобы, во-первых, вычисления
требующие определенного порядка расположения данных, могли выполняться
эффективно, во-вторых, результаты имели осмысленный вид, в третьих,
последующие процессы бы пригодные исходные данные.
1.2 Метод сортировки, который многие обычно осваивают раньше других
из-за его исключительной простоты, называется пузырьковой сортировкой
(bubble sort), в рамках которой выполняются следующие действия: проход по
файлу с обменом местами соседних элементов, нарушающих заданный
порядок, до тех пор, пока файл не будет окончательно отсортирован.
1.3 Гномья сортировка (англ. Gnome sort) — алгоритм сортировки,
похожий на сортировку вставками, но в отличие от последней перед вставкой
на нужное место происходит серия обменов, как в сортировке пузырьком.
1.4 Естественная сортировка (англ. Natural sort) — простая и
эффективная модификация сортировки слиянием, которая учитывает, что
данные (или их часть) могут быть уже отсортированы. Суть её в том, что нужно
образовывать цепочки, и производить их слияние не в заранее определённом и
фиксированном порядке, а анализировать имеющиеся в массиве данные.
1.5 Запись – это совокупность элементов информации о каком-то событии
или структуре. Каждый элемент информации в записи, такой как номер
служащего, цена единицы товара или валовой объем, называется полем записи.
1.6 Ключом называется поле, содержащее величину, используемую в
правилах упорядочивания файла.
1.7 Сортировка обменом – это общий термин, используемый для
описания семейства минимальных по памяти методов сортировки, которые
меняют местами элементы списка, если предшествующий элемент больше
последующего. Просмотр файла может протекать сверху вниз или снизу вверх
или изменяться от просмотра к просмотру.
1.8 Метод парного обмена (также называемый «нечетно-четная
перестановка») состоит из различного числа «нечетных» и «четных»
просмотров.
1.9 Метод просеивания (также называемый «линейной вставкой с
обменом» или «челночной сортировкой») является самым лучшим из этих
методов. Он отличается от других методов обмена тем, что не сохраняет
фиксированной последовательности сравнений.
1.10 Быстрая сортировка – это общее название ряда алгоритмов, которые
отражают различные подходы к получению критичного параметра, влияющего
на производительность метода.
1.11 Вложенная рекурсия- напротив, если в процедуре рекурсивный
вызов не является завершающей инструкцией, то такая рекурсия называется
вложенной.
1.12 Глубина рекурсии - количество незавершенных копий исходной
процедуры, хранящихся в памяти компьютера в момент работы программы.
1.13 Массив - именованный набор данных одного типа, каждый элемент
которого имеет свой порядковый номер или 'индекс'.
1.14 Сложение массивов - при сложении массива А с массивом В
получается массив С, каждый элемент которого равен сумме соответствующих
элементов массивов А и В, т.е. C[i]=A[i]+B[i]. Вычитание происходит
аналогично.
1.15 Хвостовая рекурсия - такой способ организации алгоритма, когда
рекурсивный вызов является последней командой процедуры.
1.16 Цикл - такой способ организации алгоритма, при котором некоторый
фрагмент кода выполняется условное количество раз. Циклы - наиболее
удобный путь обработки данных, хрянящихся в массивах.
1.17 Внешняя сортировка - сортировка данных, расположенных на
внешних запоминающих устройствах.
1.18 Внутренняя сортировка - сортировка данных, расположенных в
оперативной памяти компьютера.
1.19 Методы сортировки: выбор - в массиве необходимо найти элемент с
минимальным значением и поменять его местами с первым элементом массива
(для сортировки по убыванию - это необходимо сделать с максимальным
элементом). После этого элемент с минимальным значением отыскивается
среди всех элементов, кроме первого, и меняется значениями со вторым
элементом массива и т.д. В результате все элементы выстраиваются по порядку.
1.20 Методы сортировки: счетчик - при сортировке массива методом
"счетчика" заводится дополнительный массив того же размера, что и исходный.
Этот массив заполняется элементами первого массива так, что для каждого
элемента вычисляется нужный порядковый номер, то есть число меньших (при
сортировке по возрастанию) элементов первого массива.
2 ЛЕКЦИИ
Лекция № 1. Введение. Методы сортировок.
Продолжительность -2 часа
Цели лекции – ознакомить студентов с основными понятиями методов
сортировок и поиска, перечислить основные термины дисциплины.
Понятия, связанные с деревьями, широко известны и интуитивно понятны.
Тем не менее, для однозначного понимания содержимого этого раздела (и
соответствующего раздела следующей части курса) мы приведем несколько не
слишком формальных определений и примеров.
Существует несколько возможных определений дерева. Например, с точки
зрения теории графов деревом часто называют неориентированный граф с
выделенной вершиной (корнем), который не содержит циклов. Нас будут
интересовать не произвольные графы, а только ориентированные деревья,
причем с точки зрения программистов. Поэтому мы используем следующее
рекурсивное определение: дерево R с базовым типом T - это либо (a) пустое
дерево (не содержащее ни одной вершины), либо (b) некоторая вершина типа T
(корень дерева) с конечным (возможно, нулевым) числом связанных с ней
деревьев с базовым типом T (эти деревья называются поддеревьями дерева R).
Из этого определения, в частности, следует, что однонаправленный список
(рисунок 1) является деревом.
Рис. 1
Деревья можно представлять по-разному (это всего лишь однородная
иерархическая структура). Например, на рисунках 1 и 2 показаны два разных
способа представления одного и того же дерева, у которого базовый тип
содержит множество букв латинского алфавита. Сразу заметим, что графовое
представление
на
рисунке
2
больше
соответствует
специфике
программирования.
рис. 2
Придерживаясь естественной для графового представления терминологии,
мы будем называть связи между поддеревьями ветвями, а корень каждого
поддерева - вершиной. Упорядоченным деревом называется такое, у которого
ветви, исходящие из каждой вершины, упорядочены. Например, два
упорядоченных дерева на рисунке 3 различаются.
Рис.3
По определению, корень дерева находится на уровне 0, а все вершины
дерева, непосредственно связанные с вершиной уровня i, находятся на уровне
i+1. Вершина x уровня i, непосредственно связанная с вершиной y уровня i+1,
называется непосредственным предком (или родителем) вершины y. Такая
вершина y соответственно называется непосредственным потомком (или
сыном) вершины x. Вершина без непосредственных потомков называется
листовой (или терминальной), нелистовые вершины называются внутренними.
Под степенью внутренней вершины понимается число ее непосредственных
потомков. Если все вершины имеют одну и ту же степень, то она полагается
степенью дерева. На самом деле, всегда можно добиться того, чтобы любая
вершина дерева имела одну и ту же степень путем добавления специальных
вершин в тех точках, где отсутствуют поддеревья (рисунок 4).
Число вершин (или ветвей), которые нужно пройти от корня к вершине x,
называется длиной пути к вершине x. Высотой (или глубиной) дерева будем
называть максимальную длину его вершины.
Рис.4
Контрольные вопросы к лекции №1
1. Цель дисциплины «Методы сортировок и поиска».
2. Перечислите возможные определения деревьев.
3. Что такое граф?
4. Что называется длиной пути к вершине?
5. Что называется степенью внутренней вершины?
Лекция № 2. Сортировка методом «пузырька».
Продолжительность – 2 часа
Цель лекции- ознакомить студентов с простейшим видом сортировки, со
спецификой алгоритма сортировки.
Данный метод относится к классу простейших, занимая в нем последнее
место по производительности. Тем не менее, он очень широко известен,
видимо, благодаря своему одному легко запоминающемуся названию – метод
всплывающего пузырька (или тонущего шарика, если кому-то так больше
нравится). Работа алгоритма действительно похожа на всплывание наверх
пузырьков воздуха: сначала на самый верх всплывает самый легкий элемент,
потом за ним – чуть более тяжелый и т.д.
Пусть имеется n элементов а1 а2, а3, . . ., аn, расположенных в ячейках
массива. Для простоты будем считать, что сам элемент совпадает с его ключом.
Алгоритм состоит в повторении n-1 шага, на каждом из которых в оставшемся
необработанном наборе за счет попарного сравнения соседних элементов
отыскивается минимальный элемент.
Шаг 1. Сравниваем аn с аn-1 и если аn < аn-1 то меняем их местами, потом
сравниваем аn-1 с аn-2 и, возможно, переставляем их, сравниваем аn-2 и аn-3 и
т.д. до сравнения и, возможно, перестановки а2 и а1. В результате на первом
месте в массиве оказывается самый минимальный элемент, который в
дальнейшей сортировке не участвует
Шаг 2. Аналогично сравниваем аn с аn-1, аn-1 с аn-2 и т.д., а3 с а2, в
результате чего на месте а2 оказывается второй наименьший элемент, который
вместе с а1 образует начальную часть упорядоченного массива
Шаг 3. Аналогичными сравнениями и перестановками среди элементов а3,
а4, … , аn находится наименьший, который занимает место а3
.....
Шаг n-1. К этому моменту первые
n-2
элемента в массиве уже
упорядочены и остается “навести порядок” только между двумя последними
элементами аn-1 и аn. На этом сортировка заканчивается.
Пример. Дано 6 элементов – целые числа 15, 33, 42, 07, 12, 19.
а1
шаг 1 15
15
15
15
07
а2
33
33
33
07
15
а3
42
42
07
33
33
а4
07
07
42
42
42
а5
12
12
12
12
12
а6
19
19
19
19
19
шаг 2 07
07
07
07
15
15
15
12
33
33
12
15
42
12
33
33
12
42
42
42
19
19
19
19
шаг 3 07
07
07
12
12
12
15
15
15
33
19
19
19
33
33
42
42
42
шаг 4 07
12
15
19
33
42
Выполняемые операции
сравнение 19 и 12, обмена нет
сравнение 12 и 07, обмена нет
сравнение 07 и 42, меняем их
сравнение 07 и 33, меняем их
сравнение 07 и 15, меняем их; 07 наименьший
сравнение 19 и 12, обмена нет
сравнение 12 и 42, меняем их
сравнение 12 и 33, меняем их
сравнение 12 и 15, меняем их, 12 –второй
наим.
сравнение 19 и 42, меняем их
сравнение 19 и 33, меняем их
сравнение 19 и 15, обмена нет, 15 – третий
наим.
сравнение 42 и 33, обмена нет
07
12
15
19
33
42
шаг 5 07
12
15
19
33
42
07
12
15
19
33
42
сравнение 33 и 19, обмена нет, 19 –
четвертый элем.
сравнение 42 и 33, обмена нет, сортировка
закончена
Итого, для шести элементов сделано 5+4+3+2+1 = 15 сравнений и 8
перестановок.
В общем случае, на каждом из n-1 шагов выполняется в среднем n/2
сравнений, поэтому оценка для числа сравнений выражается соотношением
n(n-1)/2, т.е. данный метод относится к классу O(n2). Аналогично, число
перестановок тоже пропорционально n2. Несмотря на то, что было предложено
несколько улучшений данного метода (есть очень красивые названия –
например, шейкер-сортировка), он остается самым неэффективным. Уже для
1000 элементов число сравнений выражается внушительной величиной порядка
500 тысяч.
Программная реализация включает двойной цикл: внешний реализует
основные шаги алгоритма, внутренний сравнивает и переставляет элементы,
начиная с конца массива.
for i := 2 to n do
begin
for j := n downto i do
if a[j-1] > a[j] then
begin temp := a[j-1]; a[j-1] := a[j]; a[j] := temp; end;
end;
Контрольные вопросы к лекции №2
1. В чем заключается особенность метода сортировки «пузырьком»?
2. К какому классу методов сортировок относится данный метод?
3. Как еще по-другому называется данный метод?
4. Какой цикл включает в себя данный метод сортировки?
5. Является ли этот метод сортировки эффективным?
Лекция №3 Шейкерная сортировка
Продолжительность – 2 часа
Цели лекции- ознакомить студентов шейкерной сортировкой, основными
принципами метода.
Для алгоритма прямого обмена (пузырьковая сортировка) естественным
усовершенствованием является алгоритм шейкерной сортировки. В основе
данного алгоритма лежат следующие положения. Следует отказаться от жестко
заданного числа повторений внешнего цикла, рассчитанного, по сути, на
наихудший случай, внешний цикл должен был быть завершен в том случае,
если во время выполнения внутреннего цикла не была выполнена ни одна
перестановка. Однако такое усовершенствование не является решающим, как
дальнейшее ее развитие можно предложить сохранять историю на каком
именно шаге было выполнена последняя перестановка и следовательно при
последующем выполнении внутреннего цикла не имеет смысла исследовать
элементы находящиеся дальше и следовательно заведомо являющимися
упорядоченными. Дальнейшее развитие алгоритма простого обмена приводит
непосредственно к идее шейкерной сортировки: необходимо чередовать
направления прохода внутреннего цикла, таким образом можно к
омпенсировать «ассиметричность» перемещения элементов по направлению и
против движения.
Во-первых, если при движении по части массива перестановки не происходят,
то эта часть массива уже отсортирована и, следовательно, ее можно исключить
из
рассмотрения.
Во-вторых, при движении от конца массива к началу минимальный элемент
“всплывает” на первую позицию, а максимальный элемент сдвигается только на
одну
позицию
вправо.
Эти две идеи приводят к следующим модификациям в методе пузырьковой
сортировки. Границы рабочей части массива (т.е. части массива, где
происходит движение) устанавливаются в месте последнего обмена на каждой
итерации. Массив просматривается поочередно справа налево и слева направо.
Лучший случай для этой сортировки — отсортированный массив (О(n)),
худший
—
отсортированный
в
обратном
порядке
(O(n²)).
Наименьшее число сравнений в алгоритме Шейкер-сортировки C=N-1. Это
соответствует единственному проходу по упорядоченному массиву (лучший
случай)
Вот моя версия реализации данного алгоритма на С++:
?
1
template <typename Type>
2
void sheikerSort(Type *arrayOfElements, int n){
3
int left, rigth, last;
4
left = 0;
5
last=rigth = n-1;
6
7
do{
8
for (int i = rigth; i>=left; i--){
9
if (arrayOfElements[i]<arrayOfElements[i-1]){
10
swapp(arrayOfElements[i],arrayOfElements[i-1]);
11
last = i;
12
}
13
}
14
left = last+1;
15
for (int i = left ; i<=rigth;i++){
16
if (arrayOfElements[i]<arrayOfElements[i-1]){
17
swapp(arrayOfElements[i],arrayOfElements[i-1]);
18
last = i;
19
}
20
}
21
rigth = last-1;
22
}while (left<=rigth);
23
};
Код написан в виде готовой функции на С++ с использованием шаблонов. Это
позволяет передать массив любых значений, при условии, что они
поддерживают оператор «<».
Функция swapp:
?
1
2
3
4
5
template <typename Type> void swapp(Type &a, Type &b){
Type c = a;
a = b;
b = c;
};
Контрольные вопросы к лекции №3
1. Усовершенствованным методом какого алгоритма является шейкерная
сортировка?
2. Что является лучшим случаем для данного метода сортировки?
3. Почему данный алгоритм сортировки называется шейкерным?
4. В скольких направлениях происходит сортировка в шейкерной
сортировке?
Лекция №4. Сортировка прямыми вставками
Продолжительность – 1 час
Цель лекции- дать студентам основные понятия, используемые в данном
методе сортировки, передать суть метода.
Сортировка прямыми вставками в чем-то похожа на вышеизложенные
методы.
Аналогичным образом делаются проходы по части массива, и
аналогичным же образом в его начале "вырастает" отсортированная
последовательность.
Однако в сортировке пузырьком или выбором можно было четко заявить,
что на i-м шаге элементы a[0]...a[i] стоят на правильных местах и никуда более
не переместятся. Здесь же подобное утверждение будет более слабым:
последовательность a[0]...a[i] упорядочена. При этом по ходу алгоритма в нее
будут вставляться все новые элементы.
Будем разбирать алгоритм, рассматривая его действия на i-м шаге. Как
говорилось выше, последовательность к этому моменту разделена на две части:
готовую a[0]...a[i] и неупорядоченную a[i+1]...a[n].
На следующем, (i+1)-м каждом шаге алгоритма берем a[i+1] и вставляем
на
нужное
место
в
готовую
часть
массива.
Поиск
подходящего
места
для
очередного
элемента
входной
последовательности осуществляется путем последовательных сравнений с
элементом,
стоящим
перед
ним.
В зависимости от результата сравнения элемент либо остается на текущем
месте(вставка завершена), либо они меняются местами и процесс повторяется.
Таким образом, в процессе вставки мы "просеиваем" элемент x к началу
массива, останавливаясь в случае, когда
1. Hайден элемент, меньший x или
2. Достигнуто начало последовательности.
template<class T>
void insertSort(T a[], long size) {
T x;
long i, j;
for ( i=0; i < size; i++) { // цикл проходов, i - номер прохода
x = a[i];
// поиск места элемента в готовой последовательности
for ( j=i-1; j>=0 && a[j] > x; j--)
a[j+1] = a[j];
// сдвигаем элемент направо, пока не дошли
// место найдено, вставить элемент
a[j+1] = x;
}
}
Аналогично сортировке выбором, среднее, а также худшее число
сравнений и пересылок оцениваются как Theta(n2), дополнительная память при
этом не используется.
Хорошим показателем сортировки является весьма естественное
поведение: почти отсортированный массив будет досортирован очень быстро.
Это, вкупе с устойчивостью алгоритма, делает метод хорошим выбором в
соответствующих ситуациях.
Алгоритм можно слегка улучшить. Заметим, что на каждом шаге
внутреннего цикла проверяются 2 условия. Можно объединить из в одно,
поставив в начало массива специальный сторожевой элемент. Он должен быть
заведомо меньше всех остальных элементов массива.
Тогда при j=0 будет заведомо верно a[0] <= x. Цикл остановится на
нулевом элементе, что и было целью условия j>=0.
Таким образом, сортировка будет происходить правильным образом, а во
внутреннем цикле станет на одно сравнение меньше. С учетом того, что оно
производилось Theta(n2) раз, это - реальное преимущество. Однако,
отсортированный массив будет не полон, так как из него исчезло первое число.
Для окончания сортировки это число следует вернуть назад, а затем вставить в
отсортированную последовательность a[1]...a[n].
// сортировка вставками со сторожевым элементом
template<class T>
inline void insertSortGuarded(T a[], long size) {
T x;
long i, j;
T backup = a[0];
// сохранить старый первый элемент
setMin(a[0]);
// отсортировать массив
for ( i=1; i < size; i++) {
x = a[i];
for ( j=i-1; a[j] > x; j--)
a[j+1] = a[j];
a[j+1] = x;
// заменить на минимальный
}
// вставить backup на правильное место
for ( j=1; j<size && a[j] < backup; j++)
a[j-1] = a[j];
// вставка элемента
a[j-1] = backup;
}
Функция setmin(T& x) должна быть создана пользователем. Она заменяет x
на элемент, заведомо меньший(меньший или равный, если говорить точнее)
всех элементов массива.
Контрольные вопросы к лекции №4
1. Чем данный метод сортировки отличается от метода сортировки
«пузырьком» и метода сортировки выбором?
2. Путем чего осуществляется поиск подходящего места для
очередного элемента входной последовательности?
3. Как оценивается среднее и худшее число сравнений и пересылок?
4. Что такое «сторожевой элемент»?
5. Что выполняет функция setmin(T& x)?
Лекция №5. Сортировка Шелла.
Продолжительность -2 часа
Цель лекции – ознакомить студентов с данным методом сортировки
элементов, с основными действиями сортировки.
Метод Шелла является улучшенным вариантом метода вставок. Поскольку
метод вставок дает хорошие показатели качества для небольших или почти
упорядоченных наборов данных, метод Шелла использует эти свойства за счет
многократного применения метода вставок.
Алгоритм метода Шелла состоит в многократном повторении двух
основных действий:
объединение нескольких элементов исходного массива по
некоторому правилу
сортировка этих элементов обычным методом вставок
Более подробно, на первом этапе группируются элементы входного
набора с достаточно большим шагом. Например, выбираются все 1000-е
элементы, т.е. создаются группы:
группа 1: 1, 1001, 2001, 3001 и т.д.
группа 2: 2, 1002, 2002, 3002 и т.д.
группа 3: 3, 1003, 2003, 3003 и т.д.
.....................
группа 1000: 1000, 2000, 3000 и т.д.
Внутри каждой группы выполняется обычная сортировка вставками, что
эффективно за счет небольшого числа элементов в группе.
На втором этапе выполняется группировка уже с меньшим шагом,
например - все сотые элементы. В каждой группе опять выполняется обычная
сортировка вставками, которая эффективна за счет того, что после первого
этапа в каждой группе набор данных будет уже частично отсортирован.
На третьем этапе элементы группируются с еще меньшим шагом, например
– все десятые элементы. Выполняется сортировка, группировка с еще меньшим
шагом и т.д.
На последнем этапе сначала выполняется группировка с шагом 1,
создающая единственный набор данных размерности n, а затем - сортировка
практически отсортированного набора.
Пример. Исходный набор: 15 – 33 – 42 – 07 – 12 - 19
Выполняем группировку с шагом 3, создаем три группы по 2 элемента и
сортируем каждую из них отдельно:
группа 1: 15 – 07 => 07 – 15 (1 сравнение, 1 пересылка)
группа 2: 33 – 12 => 12 – 33 (1 сравнение, 1 пересылка)
группа 3: 42 – 19 => 19 – 42 (1 сравнение, 1 пересылка)
Новый набор чисел: 07 – 15 – 12 – 33 – 19 – 42
Группировка с меньшим шагом 2 дает 2 группы по 3 элемента, которые
сортируются отдельно:
группа 1: 07 – 12 – 19 => уже упорядочена (2 сравнения, 0 пересылок)
группа 2: 15 – 33 – 42 => уже упорядочена (2 сравнения, 0 пересылок)
Новый набор чисел: 07 – 12 – 19 – 15 – 33 – 42
Последняя группировка с шагом 1 дает сам набор чисел; к нему
применяется сортировка вставками с 5-ю сравнениями и только одной
пересылкой, после чего получаем искомый результат.
Итого – 12 сравнений и 4 пересылки, что в общем-то не лучше чем у
простых методов. Однако, здесь надо учесть два фактора.
Фактор 1 (общий). Улучшенные методы показывают свою эффективность
именно для больших наборов данных (сотни, тысячи и т.д. элементов). Для
очень малых наборов (как в примере) они могут давать даже худшие
результаты.
Фактор 2 (специфический). Эффективность метода Шелла существенно
зависит
от
выбора
последовательности
шагов
группировки.
Эта
последовательность обязательно должна быть убывающей, а последний шаг
обязательно
равен
1.
В
настоящее
время
неизвестна
наилучшая
последовательность шагов, обеспечивающая наименьшую трудоемкость. На
основе многочисленных экспериментов установлено, что число шагов
группировки надо выбирать по формуле
[(log
2
n)] – 1, где
скобки [ ]
используются для обозначения целой части числа, а в качестве самих
последовательностей рекомендуется один из следующих наборов (обращаю
внимание: для удобства восприятия шаги даются в обратном порядке):
1, 3, 5, 9, 17, 33, . . . (общая формула: tk = (2* tk-1) –1 )
1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 . . .
(общая формула: tk = (2* tk-1) +1, а еще проще – (2k – 1)).
В соответствии с этими рекомендациями, в предыдущем примере надо
взять лишь 2 шага группировки со значениями 3 и 1. В этом случае потребуется
лишь 8 сравнений и 5 пересылок.
Что касается программной реализации, то по сравнению с методом вставок
потребуется организовать еще один самый внешний цикл для выполнения
группировок элементов с убывающими шагами. Сами шаги можно вычислять
по приведенным выше формулам, а можно хранить в предварительно
подготовленном
вспомогательном
массиве.
Никакого
выделения
сгруппированных элементов в отдельные массивы не производится, вся работа
выполняется за счет изменения индексов элементов.
for m := 1 to t do
{t – число шагов группировки, m – номер шага}
begin
k := h [m];
{выбор величины шага группировки из заданного массива}
for i := k + 1 to n do
{сортировка вставками внутри каждой группы}
begin
temp := a [i]; j := i – k;
while (j > 0) and (temp < a [j]) do
begin
a [j + k] := a [j]; j := j – k;
end;
a [j + k] := temp;
end;
end;
Оценка трудоемкости метода Шелла выражается соотношением O(n1,2),
что лучше чем у простейших методов, особенно при больших n.
Контрольные вопросы к лекции №5
1. В чем заключается суть метода Шелла?
2. От чего взял название данный метод сортировки?
3. К какому классу сортировок относится данный метод?
4. Как производится оценка трудоемкости данного метода?
5. От чего существенно зависит эффективность метода Шелла?
Лекция №6. Сортировка с разделением (быстрая сортировка)
Продолжительность – 2 часа
Цель лекции-ознакомить студентов с одним из методов улучшенной
сортировки, перечислить основные термины и понятия, разьяснить алгоритм
сортировки.
Метод сортировки разделением был предложен Чарльзом Хоаром (он любит
называть себя Тони) в 1962 г. Этот метод является развитием метода простого
обмена и настолько эффективен, что его стали называть "методом быстрой
сортировки - Quicksort".
Основная идея алгоритма состоит в том, что случайным образом выбирается
некоторый элемент массива x, после чего массив просматривается слева, пока
не встретится элемент a[i] такой, что a[i] > x, а затем массив просматривается
справа, пока не встретится элемент a[j] такой, что a[j] < x. Эти два элемента
меняются местами, и процесс просмотра, сравнения и обмена продолжается,
пока мы не дойдем до элемента x. В результате массив окажется разбитым на
две части - левую, в которой значения ключей будут меньше x, и правую со
значениями ключей, большими x. Далее процесс рекурсивно продолжается для
левой и правой частей массива до тех пор, пока каждая часть не будет
содержать в точности один элемент. Понятно, что как обычно, рекурсию можно
заменить итерациями, если запоминать соответствующие индексы массива.
Проследим этот процесс на примере нашего стандартного массива (таблица
2.6).
Таблица 2.6. Пример быстрой сортировки
Начальное состояние массива
8 23 5 65
|44| 33 1 6
Шаг 1 (в качестве x выбирается a[5])
|--------|
8 23 5 6 44
33 1 65
|---|
8 23 5 6 1 33
44 65
Шаг 2 (в подмассиве a[1], a[5] в
качестве x выбирается a[3])
8 23 |5| 6 1
33 44 65
|--------|
1 23 5 6 8 33
44 65
|--|
1 5 23 6 8 33
44 65
Шаг 3 (в подмассиве a[3], a[5] в
качестве x выбирается a[4])
1 5 23 |6| 8
33 44 65
|----|
1 5 8 6 23 33
44 65
Шаг 4 (в подмассиве a[3], a[4]
выбирается a[4])
1 5 8 |6| 23
33 44 65
|--|
1 5 6 8 23 33
44 65
Алгоритм недаром называется быстрой сортировкой, поскольку для него
оценкой числа сравнений и обменов является O(n?log n). На самом деле, в
большинстве утилит, выполняющих сортировку массивов, используется именно
этот алгоритм.
Контрольные вопросы к лекции №6
1. В чем заключается основная идея метода быстрой сортировки?
2. Кем был предложен данный метод сортировки?
3. Почему данный метод сортировки называется быстрым?
4. Приведите пример сортировки элементов с помощью метода быстрой
сортировки?
Лекция №7. Пирамидальная сортировка
Продолжительность -2 часа
Цель лекции – дать студентам полное описание метода пирамидальной
сортировки.
Пирамидальная сортировка была предложена Дж. Уильямсом в 1964 году.
Это алгоритм сортировки массива произвольных элементов; требуемый им дополнительный объём памяти не зависит от количества исходных данных. Время работы алгоритма -в среднем, а также в лучшем и худшем случаях.
В данной лекции будет рассмотрен базовый вариант этого алгоритма, не
отличающийся большой скоростью (в частности, работающий несущественно
быстрее сортировки слиянием). Более быстрые (и сложные) варианты алгоритма вы можете найти в Интернете с помощью поисковых запросов типа «Heap
sort modification».
Везде далее будем считать, что первый элемент массива имеет индекс 0.
Идея алгоритма
Для того, чтобы прояснить всё дальнейшее изложение, в двух словах опишу идею алгоритма.
 Пирамида — двоичное дерево, в котором значение каждого элемента больше либо равно значений дочерних элементов.
 Заполнив дерево элементами в произвольном порядке, можно легко
его отсортировать (легче, чем исходный список элементов), превратив в
пирамиду.
 Самый большой элемент пирамиды находится в её вершине.
 Отделяем вершинный элемент, и записываем его в конец результирующего массива.
 На место вершинного элемента записываем элемент из самого нижнего уровня дерева.
 Восстанавливаем (пересортировываем) пирамиду.
 Самый большой элемент из оставшихся снова в вершине. Снова отделяем его и записываем его в качестве предпоследнего элемента результата, и так далее...
 Весь фокус алгоритма в том, что пирамида без дополнительных затрат хранится прямо в исходном массиве. По мере того, как размер пирамиды уменьшается, она занимает всё меньшую часть массива, а результат
сортировки записывается начиная с конца массива на освободившиеся от
пирамиды места.
Двоичное дерево
Двоичное дерево — структура данных, в которой каждый элемент имеет
левого и/или правого потомка, либо вообще не имеет потомков. В последнем
случае элемент называется листовым.
Если элемент A имеет потомка B, то элемент A называется родителем элемента B. В двоичном дереве существует единственный элемент, который не
имеет родителей; такой элемент называется корневым.
Пример двоичного дерева показан на рисунке 1:
Рисунок 1. Пример двоичного дерева
Почти заполненным двоичным деревом называется двоичное дерево, обладающее тремя свойствами:
1.
все листовые элементы находятся в нижнем уровне, либо
в нижних двух уровнях;
2.
все листья в уровне заполняют уровень слева;
3.
все уровни (кроме, быть может, последнего уровня) полностью заполнены элементами.
Дерево на рисунке 1 не является почти заполненным, так как уровень 4 не
заполнен слева (элемент g «мешает»), и третий уровень (не являющийся последним), не заполнен полностью.
Почти заполненное двоичное дерево можно хранить в массиве без дополнительных затрат. Для этого достаточно перенумеровать элементы каждого
уровня слева-направо:
Рисунок 2. Пример нумерации элементов почти заполненного дерева
Нетрудно видеть, что при такой нумерации потомки узла с номером i имеют номера
и
(если они есть). Родитель узла имеет номер
:
Рисунок 3. Пример нахождения потомков и родителя в массиве
Пирамида
Возрастающей пирамидой называется почти заполненное дерево, в котором значение каждого элемента больше либо равно значений всех его потомков. Аналогично, в убывающей пирамиде значение каждого элемента меньше
либо равно значений потомков.
Пример возрастающей пирамиды показан на рисунке:
Рисунок 4. Пример возрастающей пирамиды
Очевидно, самое большое значение в возрастающей пирамиде имеет корневой элемент. Однако, из свойств пирамиды не следует, что значения элементов уменьшаются с увеличением уровня. В частности, на рисунке 4 элемент со
значением 3 на четвёртом уровне больше значений всех элементов, кроме одного, находящихся на третьем уровне.
Важной операцией является отделение последнего (в смысле нумерации)
элемента от пирамиды. Нетрудно доказать, что в этом случае двоичное дерево
остаётся почти заполненным, и все свойства пирамиды сохраняются.
Просеивание вверх
Рассмотрим теперь задачу присоединения элемента с произвольным значением к возрастающей пирамиде. Если просто добавить элемент в конец массива, то свойство пирамиды (значение любого элемента
значения его родите-
ля) может быть нарушено. Для восстановления свойства пирамиды к добавленному элементу применяется процедура просеивания вверх, которая описывается
следующим алгоритмом:
1. если элемент корневой, или его значение
значения родителя, то конец;
2. меняем местами значения элемента и его родителя;
3. переходим к родителю, и выполняем для него этот же алгоритм, начиная
с пункта 1.
Пример просеивания вверх добавленного элемента показан на рисунке:
Рисунок 5. Процесс просеивания вверх добавленного элемента
После выполнения данной процедуры свойство пирамиды будет восстановлено,
так как:
 Если вновь добавленный элемент больше родителя, то он больше и второго потомка родителя (если второй потомок есть), так как до добавления
нового элемента было выполнено свойство пирамиды, и родитель был не
меньше своего потомка (на рисунке 5 слева: семёрка больше своего родителя — четвёрки, — поэтому она больше и второго потомка четвёрки —
тройки). После обмена значений элемента и родителя свойство пирамиды
будет восстановлено в поддереве, корнем которого является родитель
с новым значением (например, после обмена местами семёрки и четвёрки,
семёрка тройка и четвёрка будут образовывать правильное поддерево).
 В результате обмена свойство пирамиды может быть нарушено в отношении родителя и родителя родителя (так как значение родителя стало больше). Процедура вызывается для родительского узла, чтобы восстановить
это свойство.
Исходный код процедуры просеивания вверх я не привожу, так как он нам не
понадобится.
Просеивание вниз
Что делать, если нам нужно заменить корневой элемент на какой-либо другой? В этом случае пирамидальную структуру двоичного дерева можно восстановить с помощью процедуры просеивания вниз:
1. если элемент листовой, или его значение
нец;
значений потомков, то ко-
2. иначе меняем местами значения элемента и его потомка, имеющего максимальное значение;
3. переходим к изменившемуся потомку, и выполняем для него этот же алгоритм, начиная с пункта 1.
Пример просеивания вниз показан на рисунке:
Рисунок 6. Процесс просеивания вниз изменённого корневого элемента.
Красным цветом показан текущий элемент
Для тех, кто попадает на эту страницу по поисковым запросам типа «пирамидальная сортировка, параллельный алгоритм»: к сожалению, попытки распараллелить этот алгоритм приводят к его существенному усложнению, и снижению эффективности вычислений.
Так как пирамидальную сортировку целесообразно применять для небольших объёмов сортируемых данных (когда они умещаются в кэш процессора),
накладные расходы параллельной программы будут слишком велики.
Контрольные вопросы к лекции №7
1. В чем заключается идея пирамидальной сортировки?
2. Для каких данных целесообразно применять пирамидальную
сортировку?
3. Почему данный метод сортировки назвали пирамидальным?
4. Что является важной операцией в методе пирамидальной сортировки
данных?
5. К какому классу сортировок относится данный метод сортировки?
Лекция №8 Метод сортировки массивов
Продолжительность -2 часа
Цель лекции – ознакомить студентов со способами сортировки массивов, с
отличительной особенностью метода сортировки массивов, привести примеры
сортировки.
Для решения многих задач удобно сначала упорядочить данные по
определенному признаку, так можно ускорить поиск некоторого объекта.
Например, в преферансе игроки раскладывают карты по мастям и по значению.
Так легче определить, каких карт не хватает. Или возьмем любой
энциклопедический словарь - статьи в нем упорядочены в алфавитном порядке.
Перегруппирование заданного множества объектов в определенном
порядке называют сортировкой.
Почему сортировке уделяется большое внимание? Вы это поймете,
прочитав цитаты двух великих людей.
"Даже если бы сортировка была почти бесполезна, нашлась бы масса
причин заняться ею! Изобретательные методы сортировки говорят о том, что
она и сама по себе интересна как объект исследования." /Д. Кнут/
"Создается впечатление, что можно построить целый курс
программирования, выбирая примеры только из задач сортировки." /Н. Вирт/
Отличительной особенностью сортировки является то обстоятельство, что
эффективность алгоритмов, реализующих ее, прямо пропорциональна
сложности понимания этого алгоритма. Другими словами, чем легче для
понимания метод сортировки массива, тем ниже его эффективность.
Сегодня существует множество методов сортировки, но для понимания
сути сортировки рассмотрим некоторые из них.
Но прежде чем перейти к рассмотрению конкретного алгоритма той или
иной сортировки немного вспомним материал, который пригодится нам в
дальнейшем.
Задача. Даны две целочисленные переменные х и y. Составить фрагмент
программы, после выполнения которого значения этих переменных
распределяются в порядке убывания.
Обмен значений переменных нужно производить лишь в том случае, если х<у.
Для того чтобы не потерять начальное значение переменной х, введем
дополнительную переменную t.
if x<y
then
begin
t:=x;
x:=y;
y:=t;
end;
Задача. Составить фрагмент программы поиска максимального числа из
трех введенных с клавиатуры чисел.
Пусть а, b, c - вводимые с клавиатуры числа, Max - максимальное из их
значений. На первом шаге предположим , что а - максимальное из чисел и
поэтому Max:=a. Затем сравним значение предполагаемого максимума со
значениями переменных b и с. Если значение m окажется меньше, чем значение
очередной переменной, то переопределим значение максимума.
...
m:=a;
if m<b
then
m:=b;
if m<c
then
m:=c;
...
Задача. Дан массив а, состоящий из 10 элементов. Составить программу
поиска максимального элемента массива.
Используем идею предыдущей задачи. Перед началом поиска выберем
условно в качестве максимального первый элемент массива Max:=a[1]. Затем по
очереди каждый элемент массива сравним со значением переменной m. Если он
окажется больше, то изменим значение Max. После анализа всех элементов
массива переменная Max содержит значение максимального элемента массива.
...
Max:=a[1];
for i := 2 to 10 do
if Max<a[i]
then
Max := a[i];
...
До написания программ Вам необходимо выполнить некоторую
подготовительную работу - написать шаблон программы следующего
содержания:
Рrogram Sorting;
Сonst
n = ... ; {количество элементов в массиве}
Type
TArray = array [1..n] of integer;
Procedure FillArray (Var a: TArray);
Var
i: integer;
Begin
for i: = 1 to n do
a [i] := Random(100);
End; {конец процедуры}
Procedure PrintArray (a: TArray);
Var
i: integer;
Begin
for i: = 1 to n do
write (a [i]: 3, ' ');
writeln;
End;
Begin {Главная программа}
writeln('Сортировка МЕТОДОМ . . .');
writeln('Заполняем исходный массив: ');
FillArray (a);
PrintArray (a);
AnySort (a, b);{имя процедуры, реализующей данный метод}
writeln('Отсортированный массив: ');
PrintArray (b);
End.
Для реализации различных методов сортировки Вам необходимо подготовить
несколько вспомогательных процедур и функций.
1. Функция, которая ищет минимальный элемент правее некоторого
заданного и возвращает его номер в качестве результата. Аргументами
функции являются номер элемента массива и обрабатываемый массив;
2. Большинство методов сортировок основаны на обмене двух чисел. Для
этой цели предназначена процедура, которая в качестве параметров берет
два числа и меняет их значения;
3. Также Вам пригодится процедура, которая берет элемент с индексом i,
перемещает его на место элемента с номером j. А все элементы, которые
имеют индексы от j до i-1 сдвигает на одну позицию вправо.
Контрольные вопросы к лекции №8
1. Опишите основные методы сортировки массивов?
2. Как можно представить массив при сортировке?
3. Какие вспомогательные процедуры и функции нужно подготовить
чтобы отсортировать массив?
4. Что пропорционально сложности понимания алгоритма?
3 ПРАКТИЧЕСКИЕ ЗАНЯТИЯ
Основная
наша
задача – продемонстрировать различные
методы
сортировки и выделить наиболее эффективные из них. Сортировка –
достаточно хороший пример задачи, которую можно решать с помощью
многих различных алгоритмов. Каждый из них имеет и свои достоинства,
и свои недостатки, и выбирать алгоритм нужно, исходя из конкретной
постановки задачи.
В
общем
сортировку
следует
понимать
как
процесс
перегруппировки заданного множества объектов в некотором определенном
порядке. Цель
таком
сортировки – облегчить последующий поиск элементов в
отсортированном
фундаментальная
объектами
в
множестве. Это
деятельность. Мы
телефонных
книгах, в
встречаемся
списках
почти
с
универсальная,
отсортированными
подоходных
налогов, в
оглавлениях книг, в библиотеках, в словарях, на складах – почти везде, где
нужно искать хранимые объекты.
Таким образом, разговор о сортировке вполне уместен и важен,
если речь идет об обработке данных. Наш первоначальный интерес к
сортировке основывается на том, что при построении алгоритмов мы
сталкиваемся со многими весьма фундаментальными приемами. Почти не
существует
методов, с
которыми
не
приходится
встречаться
при
обсуждении этой задачи. В частности, сортировка – это идеальный объект
для демонстрации огромного разнообразия алгоритмов, все они изобретены
для одной и той же задачи, многие в некотором смысле оптимальны,
большинство имеет свои достоинства. Поэтому это еще и идеальный
объект, демонстрирующий
необходимость
анализа
производительности
алгоритмов. К тому же на примерах сортировок можно показать, как путем
усложнения алгоритма, хотя под рукой и есть уже очевидные методы,
можно добиться значительного выигрыша в эффективности.
Выбор алгоритма зависит от структуры обрабатываемых данных – это
почти закон, но в случае сортировки такая зависимость столь глубока, что
соответствующие методы разбили на два класса – сортировку массивов и
сортировку
внутренней
файлов
и
(последовательностей). Иногда
внешней
сортировкой, поскольку
их
массивы
называют
хранятся
в
быстрой, оперативной, внутренней памяти машины со случайным доступом,
а файлы обычно размещаются в более медленной, но и более емкой
внешней
памяти, на
устройствах, основанных
на
механических
перемещениях (дисках или лентах).
Практическое занятие № 1. Сортировка «пузырьком»
Методические указания к выполнению практического задания № 1
В данном методе мы повторяем проходы по массиву, сдвигая каждый
раз наименьший элемент оставшейся последовательности к левому концу
массива. Если
мы
будем
рассматривать
как
вертикальные, а
не
горизонтальные построения, то элементы можно интерпретировать как
пузырьки в чане с водой, причем вес каждого соответствует его ключу.
Таблица 1.1. Пример пузырьковой сортировки.
2
3
4
5
6
7
8
44
06
06
06
06
06
06
06
55
44
12
12
12
12
12
12
12
55
44
18
18
18
18
18
42
12
55
44
42
42
42
42
94
42
18
55
44
44
44
44
18
94
42
42
55
55
55
55
06
18
94
67
67
67
67
67
67
67
67
94
94
94
94
94
I=1
Такой
метод
сортировки
известен
под
именем
сортировка”. Он представлен в программе 1.1.
ПРОГРАММА 2.4. ПУЗЫРЬКОВАЯ СОРТИРОВКА.
PROGRAM BS;
VAR I,J,X,N:INTEGER;
A:ARRAY[0..50] OF INTEGER;
BEGIN
WRITELN('Введи длину массива');
READ(N);
WRITELN('Введи массив');
FOR I:=1 TO N DO READ(A[I]);
FOR I:=2 TO N DO FOR J:=N DOWNTO I DO IF A[J-1]>A[J] THEN BEGIN
X:=A[J-1];
A[J-1]:=A[J];
A[J]:=X
END;
WRITELN('Результат:');
FOR I:=1 TO N DO WRITE(A[I],' ')
END.
“пузырьковая
Улучшения этого алгоритма напрашиваются сами собой:
1. Запоминать, были или не были перестановки в процессе
некоторого прохода.
2. Запоминать не только сам факт, что обмен имел место, но и
положение (индекс) последнего обмена.
3. Чередовать направление последовательных просмотров.
Получающийся
сортировкой
при
этом
алгоритм
мы
назовем
(ShakerSoft). Таблица 2.4. иллюстрирует
“шейкерной”
сортировку этим
способом.
Таблица 1.2. Пример шейкерной сортировки.
L=2
3
3
4
4
R=8
8
7
7
4
44
06
06
06
06
55
44
44
12
12
12
55
12
44
18
42
12
42
18
42
94
42
55
42
44
18
94
18
55
55
06
18
67
67
67
67
67
94
94
94
Dir =
Анализ пузырьковой и шейкерной сортировок. Число сравнений в
строго обменном алгоритме
C = (n2 – n)/2,
а минимальное, среднее и максимальное число перемещений элементов
(присваиваний) равно соответственно
M = 0, Mavg = 3*(n2 – n)/2, Mmax = 3*(n2 – n)/4
Анализ же улучшенных методов, особенно шейкерной
довольно сложен. Минимальное число сравнений
сортировки
Cmin = n – 1. Кнут считает, что улучшенной пузырьковой сортировки
среднее число проходов пропорционально
n – k1n1/2, а среднее число
сравнений пропорционально ½(n2 – n(k2 +ln n)).
ПРОГРАММА 1.2. ШЕЙКЕРНАЯ СОРТИРОВКА.
PROGRAM SS;
VAR
J,L,K,R,X,N,I:INTEGER;
A:ARRAY[0..50] OF INTEGER;
BEGIN
WRITELN('Введи длину массива’);
READ(N);
WRITELN('Введи массив');
FOR I:=1 TO N DO READ(A[I]);
L:=2;
R:=N;
K:=N;
REPEAT
FOR J:=R DOWNTO L DO IF A[J-1]>A[J] THEN BEGIN
X:=A[J-1];
A[J-1]:=A[J];
A[J]:=X;
K:=J
END;
L:=K+1;
FOR J:=L TO R DO IF A[J-1]>A[J] THEN BEGIN
X:=A[J-1];
A[J-1]:=A[J];
A[J]:=X;
K:=J
END;
R:=K-1
UNTIL L>R;
WRITELN('Результат:');
FOR I:=1 TO N DO WRITE(A[I],' ')
END.
Фактически в пузырьковой сортировке нет ничего ценного, кроме ее
привлекательного названия. Шейкерная же сортировка широко используется
в тех случаях, когда известно, что элементы почти упорядочены – на
практике это бывает весьма редко.
Задание на практическое занятие №1
1. Отработать методы сортировки данных выше указанными методами,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа №2. Метод прямого выбора.
Методические указания к выполнению практического задания № 2
Этот прием основан на следующих принципах:
1.
Выбирается элемент с наименьшим ключом.
2.
Он меняется местами с первым элементом а1.
3.
Затем
этот
процесс повторяется
с
оставшимися
n-1
элементами, n-2 элементами и т.д. до тех пор, пока не останется один,
самый большой элемент.
Процесс работы этим методом с теми же восемью ключами, что и в
таблице 2.1., приведен в таблице 2.2. Алгоритм формулируется так:
FOR I:= 1 TO n-1 DO
Присвоить k индекс наименьшего из a[I] … a[n];
Поменять местами a[I] и a[k];
END
Таблица 2.1. Пример сортировки с помощью прямого выбора
Начальные
44
55
12
42
94
18
06
67
I=2
06
55
12
42
94
18
44
67
I=3
06
12
55
42
94
18
44
67
I=4
06
12
18
42
94
55
44
67
I=5
06
12
18
42
94
55
44
67
I=6
06
12
18
42
44
55
94
67
I=7
06
12
18
42
44
55
94
67
I=8
06
12
18
42
44
55
94
67
ключи
Такой
некотором
метод
смысле
сортировки – его
противоположен
называют
прямым
выбором – в
прямому включению. Полностью
алгоритм прямого выбора приводится в программе 2.1.
ПРОГРАММА 2.1. СОРТИРОВКА С ПОМОЩЬЮ ПРЯМОГО
ВЫБОРА.
PROGRAM SS;
VAR I,J,R,X,N:INTEGER;
A:ARRAY[0..50] OF INTEGER;
BEGIN
WRITELN('Введи длину массива');
READ(N);
WRITELN('Введи массив');
FOR I:=1 TO N DO READ(A[I]);
FOR I:=1 TO N-1 DO BEGIN
R:=I;
X:=A[I];
FOR J:=I+1 TO N DO IF A[J]<X THEN BEGIN
R:=J;
X:=A[R]
END;
A[R]:=A[I];
A[I]:=X
END;
WRITELN('Результат:');
FOR I:=1 TO N DO WRITE(A[I],' ')
END.
Анализ прямого выбора. Число сравнений ключей (С), очевидно не
зависит от начального порядка ключей. Для С мы имеем
C = (n2 – n)/2
Число перестановок минимально:
Mmin = 3*(n-1)
в случае изначально упорядоченных ключей и максимально
Mmax = n2/4 + 3*(n-1)
Определим
Мavg . Для
достаточно
больших
n
мы
можем
игнорировать дробные составляющие и поэтому аппроксимировать среднее
число присваиваний на i-м просмотре выражением
Fi = ln i + g + 1
где g = 0.577216… - константа Эйлера.
Среднее число пересылок Mavg в сортировке
с выбором есть
сумма Fi с i от 1 до n:
Mavg = n*(g + 1) + (Si: 1<=i<=n: ln i)
)
Вновь аппроксимируя эту сумму дискретных членов интегралом
Integral (1:n) ln x dx = x*(ln x – 1) = n*ln (n) – n + 1
Получаем приблизительное значение
Mavg = n*(ln (n) + g) .
Задание на практическое занятие №2
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа №3. Метод Шелла.
Методические указания к выполнению практического задания № 3
В
1959
году
Д. Шеллом
было
предложено
усовершенствование
сортировки с помощью прямого включения. Сам метод представлен на
нашем стандартном примере в таблице 2.5. Сначала отдельно сортируются
и группируются элементы, отстоящие друг от друга на расстояние 4. Такой
процесс
называется
четверной
сортировкой. В нашем
примере
восемь
элементов и каждая группа состоит из двух элементов. После первого
прохода элементы перегруппировываются – теперь каждый элемент группы
отстоит от другого на две позиции – и вновь сортируются. Это называется
двойной сортировкой. На третьем подходе идет обычная или одинарная
сортировка. На
каждом
этапе
либо
сортируется
относительно
мало
элементов, либо элементы уже довольно хорошо упорядочены и требуют
сравнительно немного перестановок.
Таблица 3.1. Сортировка с помощью
включений с уменьшающимися
расстояниями.
44
55
12
42
94
18
06
67
42
94
55
12
67
42
44
55
94
67
44
55
67
94
Четверная сортировка дает
44
18
06
Двойная сортировка дает
06
18
12
Одинарная сортировка дает
06
12
18
42
Приводимая программа не ориентирована на некоторую определенную
последовательность
расстояний. Все
t
расстояний
соответственно h1, h2, … ,ht , для них выполняются условия
ht = 1, hi+1 < hi .
Описание массива при этом выглядит так
A: ARRAY [-h1..n] OF INTEGER
Сам алгоритм для t = 4 описывается в программе 3.1.
ПРОГРАММА 3.1. СОРТИРОВКА ШЕЛЛА..
PROGRAM SHELLS;
CONST T=4;
H: ARRAY[1..4] OF INTEGER = (15,7,3,1);
VAR
I,J,K,S,X,N,M:INTEGER;
A:ARRAY[-16..50] OF INTEGER;
BEGIN
WRITELN('Введи длину массива');
READ(N);
WRITELN('Введи массив');
FOR I:=1 TO N DO READ(A[I]);
FOR M:=1 TO T DO BEGIN
K:=H[M];
S:=-K;
FOR I:=K+1 TO N DO BEGIN
X:=A[I];
J:=I-K;
IF S=0 THEN S:=-K;
INC(S);
A[S]:=X;
WHILE X<A[J] DO BEGIN
A[J+K]:=A[J];
J:=J-K
END;
A[J+K]:=X
END;
обозначаются
END;
WRITELN('Результат:');
FOR I:=1 TO N DO WRITE(A[I],' ')
END.
Анализ сортировки Шелла. Нам не известно, какие расстояния дают
наилучший результат. Но они не должны быть множителями один другого.
Справедлива такая теорема: если k-отсортированную последовательность iотсортировать, то она остается k-отсортированной. Кнут показывает, что
имеет смысл использовать такую последовательность, в которой hk-1 = 3hk +
1, ht = 1 и t = [log2 n] – 1.
Задание на практическое занятие №3
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа №4. Быстрая сортировка.
Методические указания к выполнению практического задания № 4
Основа алгоритма была разработана в 1960 году Хором (C.A.R.Hoare) и с
тех пор внимательно изучалась многими людьми. Быстрая сортировка особенно
популярна ввиду легкости ее реализации; это довольно хороший алгоритм
общего назначения, который хорошо работает во многих ситуациях, и
использует при этом меньше ресурсов, чем другие алгоритмы.
Основные достоинства этого алгоритма состоят в том, что он точечный
(использует лишь небольшой дополнительный стек), в среднем требует только
около N log N операций для того, чтобы отсортировать N элементов, и имеет
экстремально короткий внутренний цикл. Недостатки алгоритма состоят в том,
что он рекурсивен (реализация очень затруднена когда рекурсия недоступна), в
худшем случае он требует N2 операций, кроме того он очень "хрупок":
небольшая ошибка в реализации, которая легко может пройти незамеченной,
может привести к тому, что алгоритм будет работать очень плохо на некоторых
файлах.
Производительность быстрой сортировки хорошо изучена. Алгоритм
подвергался математическому анализу, поэтому существуют точные
математические формулы касающиеся вопросов его производительности.
Результаты анализа были неоднократно проверены эмпирическим путем, и
алгоритм был отработан до такого состояния, что стал наиболее
предпочтительным для широкого спектра задач сортировки. Все это делает
алгоритм стоящим более детального изучения наиболее эффективных путей его
реализации. Похожие способы реализации подходят также и для других
алгоритмов, но в алгоритме быстрой сортировки мы можем использовать их с
уверенностью, поскольку его производительность хорошо изучена.
Улучшить алгоритм быстрой сортировки является большим искушением:
более быстрый алгоритм сортировки - это своеобразная "мышеловка" для
программистов. Почти с того момента, как Хор впервые опубликовал свой
алгоритм, в литературе стали появляться "улучшенные" версии этого
алгоритма. Было опробовано и проанализировано множество идей, но все равно
очень просто обмануться, поскольку алгоритм настолько хорошо
сбалансирован, что результатом улучшения в одной его части может стать
более сильное ухудшение в другой его части. Мы изучим в некоторых деталях
три модификации этого алгоритма, которые дают ему существенное
улучшение.
Хорошо же отлаженная версия быстрой сортировки скорее всего будет
работать гораздо быстрее, чем любой другой алгоритм. Однако стоит еще раз
напомнить, что алгоритм очень хрупок и любое его изменение может привести
к нежелательным и неожиданным эффектам для некоторых входных данных.
Суть алгоритма: число операций перемены местоположений элементов
внутри массива значительно сократится, если менять местами далеко стоящие
друг от друга элементы. Для этого выбирается для сравнения один элемент х,
отыскивается слева первый элемент, который не меньше х, а справа первый
элемент, который не больше х. Найденные элементы меняются местами. После
первого же прохода все элементы, которые меньше х, будут стоять слева от х, а
все элементы, которые больше х, - справа от х. С двумя половинами массива
поступают точно также. Продолжая деление этих половин до тех пор пока не
останется в них по 1 элементу.
program Quitsort;
uses
crt;
Const
N=10;
Type
Mas=array[1..n] of integer;
var
a: mas;
k: integer;
function Part(l, r: integer):integer;
var
v, i, j, b: integer;
begin
V:=a[r];
I:=l-1;
j:=r;
repeat
repeat
dec(j)
until (a[j]<=v) or (j=i+1);
repeat
inc(i)
until (a[i]>=v) or (i=j-1);
b:=a[i];
a[i]:=a[j];
a[j]:=b;
until i>=j;
a[j]:=a[i];
a[i]:= a[r];
a[r]:=b;
part:=i;
end;
procedure QuickSort(l, t: integer);
var i: integer;
begin
if l<t then
begin
i:=part(l, t);
QuickSort(l,i-1);
QuickSort(i+1,t);
end;
end;
begin
clrscr;
randomize;
for k:=1 to 10 do
begin
a[k]:=random(100);
write(a[k]:3);
end;
QuickSort(1,n);
writeln;
for k:=1 to n do
write(a[k]:3);
readln;
end.
Пример:
60,79, 82, 58, 39, 9, 54, 92, 44, 32
60,79, 82, 58, 39, 9, 54, 92, 44, 32
9,79, 82, 58, 39, 60, 54, 92, 44, 32
9,79, 82, 58, 39, 60, 54, 92, 44, 32
9, 32, 82, 58, 39, 60, 54, 92, 44, 79
9, 32, 44, 58, 39, 60, 54, 92, 82, 79
9, 32, 44, 58, 39, 54, 60, 92, 82, 79
9, 32, 44, 58, 39, 92, 60, 54, 82, 79
9, 32, 44, 58, 39, 54, 60, 79, 82, 92
9, 32, 44, 58, 54, 39, 60, 79, 82, 92
9, 32, 44, 58, 60, 39, 54, 79, 82, 92
9, 32, 44, 58, 54, 39, 60, 79, 82, 92
9, 32, 44, 58, 54, 39, 60, 79, 82, 92
9, 32, 44, 58, 54, 39, 60, 79, 82, 92
9, 32, 39, 58, 54, 44, 60, 79, 82, 92
9, 32, 39, 58, 54, 44, 60, 79, 82, 92
9, 32, 39, 44, 54, 58, 60, 79, 82, 92
9, 32, 39, 44, 58, 54, 60, 79, 82, 92
9, 32, 39, 44, 54, 58, 60, 79, 82, 92
9, 32, 39, 44, 54, 58, 60, 79, 92, 82
9, 32, 39, 44, 54, 58, 60, 79, 82, 92
"Внутренний цикл" быстрой сортировки состоит только из увеличения
указателя и сравнения элементов массива с фиксированным числом. Это как раз
и делает быструю сортировку быстрой. Сложно придумать более простой
внутренний цикл. Положительные эффекты сторожевых ключей также
оказывают здесь свое влияние, поскольку добавление еще одной проверки к
внутреннему циклу оказало бы отрицательное влияние на производительность
алгоритма.
Самая сомнительная черта вышеприведенной программы состоит в том,
что она очень мало эффективна на простых подфайлах. Например, если файл
уже сортирован, то разделы будут вырожденными, и программа просто вызовет
сама себя N раз, каждый раз с меньшим на один элемент подфайлом. Это
означает, что не только производительность программы упадет примерно до
N2/2, но и пространство необходимое для ее работы будет около N (смотри
ниже), что неприемлемо. К счастью, есть довольно простые способы сделать
так, чтобы такой "худший" случай не произошел при практическом
использовании программы.
Когда в файле присутствуют одинаковые ключи, то возникает еще два
сомнительных вопроса. Первое, должны ли оба указателя останавливаться на
ключах равных делящему элементу или останавливать только один из них, а
второй будет проходить их все, или оба указателя должны проходить над ними.
На самом деле, этот вопрос детально изучался, и результаты показали, что
самое лучшее - это останавливать оба указателя. Это позволяет удерживать
более или менее сбалансированные разделы в присутствии многих одинаковых
ключей. На самом деле, эта программа может быть слегка улучшена
терминированием сканирования j<i, и использованием после этого quicksort(l, j)
для первого рекурсивного вызова.
Задание на практическое занятие №4
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа №5. Сортировка модифицированным методом
простого выбора
Методические указания к выполнению практического задания № 5
Этот метод основывается на алгоритме поиска минимального элемента. В
массиве А(1..n) отыскивается минимальный элемент, который ставится на
первое место . Для того, чтобы не потерять элемент , стоящий на первом месте
, этот элемент устанавливается на место минимального . Затем в усеченной
последовательности, исключая первый элемент, отыскивается минимальный
элемент и ставится на второе место и так далее n-1 раз пока не встанет на свое
место предпоследний n-1 элемент массива А, сдвинув максимальный элемент в
самый конец.
Рассмотрим алгоритмическое решение задачи на примере сортировки
некоторого массива значений по возрастанию. В соответствии с
вышеописанным методом нам необходимо несколько раз выполнять операции
поиска минимального элемента и его перестановку с другим элементом, то
есть потребуется несколько раз просматривать элементы массива с этой целью.
Количество
просмотров
элементов
массива
согласно
описанию
модифицированного метода простого выбора равно n-1, где n- количество
элементов массива. Таким образом, можно сделать вывод, что проектируемый
алгоритм сортировки будет содержать цикл, в котором будет выполняться
поиск минимального элемента и его перестановка с другим элементом.
Обозначим через i - счетчик (номер) просмотров элементов массива и
изобразим обобщенный алгоритм сортировки на рис.5.
Рис.5. Обобщенный алгоритм сортировки массива модифицированным
методом простого выбора
Отметим, что для перестановки элементов местами необходимо знать их
порядковые номера, алгоритм перестановки элементов массива был
рассмотрен ранее. Алгоритмы ввода исходного массива и вывода этого же
массива после сортировки изображены на рисунках 16 и 24 соответственно.
Алгоритм поиска в массиве минимального элемента и его номера будет
аналогичен рассмотренному в примере 10 алгоритму поиска максимального
элемента, который представлен на рис.4. Однако, в этом алгоритме будут
внесены изменения. Для того, чтобы определить какие изменения следует
внести рассмотрим выполнение сортировки данным методом с акцентом на
поиск минимального элемента на конкретном примере. Пусть исходный
массив содержит 5 элементов (2,8,1,3,7). Количество просмотров согласно
модифицированному методу простого выбора будет равно 4. Покажем в
таблице 7, как будет изменяться исходный массив на каждом просмотре.
Номер
просмотра
массива i
1
2
3
4
Таблица 5.1. Пример сортировки
Минимальный Переставляемый
Исходный
элемент
элемент
массив
Номер Значение Номер Значение
(2,8,1,3,7) 3
1
1
2
1,(8,2,3,7) 3
2
2
8
1,2,(8,3,7) 4
3
3
8
1,2,3,(8,7) 5
7
4
8
Массив после
перестановки
(1,8,2,3,7)
1,(2,8,3,7)
1,2,(3,8,7)
1,2,3,7,8
Из данных, приведенных в таблице 5.1, следует, что поиск минимального
значения в массиве на каждом просмотре осуществляется в сокращенном
массиве, который сначала начинается с первого элемента, а на последнем
просмотре массив, в котором ищется минимальный элемент начинается уже с
четвертого (или n-1) элемента. При этом можно заметить, что номер первого
элемента массива для каждого поиска и перестановки совпадает с номером
просмотра i.
Введем следующие обозначения :
К- номер минимального элемента,
J - номер элемента массива,
М и А(К)- одно и тоже значение минимального элемента массива,
i - номер переставляемого с минимальным элемента,
А(i)- значение переставляемого элемента.
Тогда циклический алгоритм сортировки модифицированным методом
простого выбора будет выглядеть следующим образом (рис.28).
Рис.6. Алгоритм сортировки массива модифицированным методом
простого выбора
Задание на практическое занятие №5
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа № 6. Пирамидальная сортировка.
Методические указания к выполнению практического задания № 6
Р. Флойд предложил перестроить линейный массив в пирамиду —
своеобразное бинарное дерево, — а затем искать минимум только среди тех
элементов, которые находятся непосредственно «под» текущим вставляемым.
Просеивание
Для начала необходимо перестроить исходный массив так, чтобы он
превратился в пирамиду, где каждый элемент «опирается» на два меньших.
Этот процесс назвали просеиванием, потому что он очень напоминает процесс
разделения некоторой смеси (камней, монет, т.п.) на фракции в соответствии с
размерам частиц: на нескольких грохотах3 последовательно задерживаются
сначала крупные, а затем всё более мелкие частицы.
Итак, будем рассматривать наш линейный массив как пирамидальную
структуру:
a[1]
a[2]
a[3]
a[4]
a[5] a[6] a[7]
a[8] a[9] a[10] a[11] a[12]
Видно, что любой элемент a[i] (1 <= i <= N div 2) «опирается» на элементы
a[2 * i] и a[2 * i + 1]. И в каждой такой тройке максимальный элемент должен
находиться «сверху». Конечно, исходный массив может и не удовлетворять
этому свойству, поэтому его потребуется немного перестроить.
Начнём процесс просеивания «снизу». Половина элементов (с ((N div 2) +
1)–го по N–й) являются основанием пирамиды, их просеивать не нужно. А для
всех остальных элементов (двигаясь от конца массива к началу) мы будем
проверять тройки a[i], a[2 * i] и a[2 * i + 1] и перемещать максимум «наверх» —
в элемент a[i].
При этом, если в результате одного перемещения нарушается
пирамидальность в другой (ниже лежащей) тройке элементов, там снова
необходимо «навести порядок» — и так до самого «низа» пирамиды:
for i := (N div 2) downto 1 do
begin
j := i;
while j <= (N div 2) do
begin
k := 2 * j;
if (k + 1 <= N) and (a[k] < a[k + 1]) then
k := k + 1;
if a[k] > a[j] then
begin
x := a[j];
a[j] := a[k];
a[k] := x;
j := k
end
else
Break
end
end;
Пример результата просеивания
Возьмем массив [1, 7, 5, 4, 9, 8, 12, 11, 2, 10, 3, 6] (N = 12).
Его исходное состояние таково (серым цветом выделено «основание»
пирамиды, не требующее просеивания):
1
7
5
4 9 8 12
1 1
2 36
1 0
После первых трёх просеиваний (a[6], a[5], a[4]) получим такую картину
(здесь и далее серым цветом выделяем участников просеивания):
1
7
5
4 9
1
8
2
21 31
6
1 0
1
7
5
1
8
1
10 9
1
2
2 1 39 6
1
10
1
7
5
81
1
1
2
14 0
24 9 3 6
11
Просеивание двух следующих элементов (a[3] и a[2]) тоже не вызовет
вопросов — для каждого из них будет достаточно только одного шага:
1
7
12 5
11
10 8
5 12
4 2 9 3
6
1
5
11 7
10 8 12
7 11
4
2
9 3
6
А вот для просеивания последнего элемента (a[1]) понадобится целых три
шага:
12 1
11 1 12
7 81 5
1 0
4 2 9 3 6
12
11 8 1
7 10 1 8 5
4 2 9 3 6
12
11
8
7 10 6 1 5
429316
Итак, мы превратили исходный массив в пирамиду: в любой тройке a[i],
a[2 * i] и a[2 * i + 1] максимум находится «сверху».
Алгоритм УлПир
Для того чтобы отсортировать массив методом Пирамиды, необходимо
выполнить такую последовательность действий:
0-й шаг: Превратить исходный массив в пирамиду (с помощью
просеивания).
1-й шаг: Для N - 1 элементов, начиная с последнего, производить
следующие действия:
 поменять местами очередной «рабочий» элемент с первым;
 просеять (новый) первый элемент, не затрагивая, однако, уже
отсортированный хвост последовательности (элементы с i–го по N–й).
Реализация алгоритма УлПир
Часть программы, реализующую нулевой шаг алгоритма УлПир, мы
привели в пункте «Просеивание», поэтому здесь ограничимся только
реализацией основного шага 1:
for i := N downto 2 do
begin
x := a[1];
a[1] := a[i];
a[i] := x;
j := 1;
while j <= ((i - 1) div 2) do
begin
k := 2 * j;
if (k + 1 <= i - 1) and (a[k] < a[k + 1]) then
k := k + 1;
if a[k] > a[j] then
begin
x := a[j];
a[j] := a[k];
a[k] := x;
j := k
end
else
Break
end
end;
Пример. Продолжим сортировку массива, для которого мы уже построили
пирамиду: [12, 11, 8, 7, 10, 6, 5, 4, 2, 9, 3, 1]. С целью экономии места мы не
будем далее прорисовывать структуру пирамиды, оставляя это несложное
упражнение читателям. Подчёркивание будет отмечать элементы,
участвовавшие в просеивании, а полужирный шрифт — элементы,
исключённые из дальнейшей обработки:
1) Меняем местами a[1] и a[12]: [1, 11, 8, 7, 10, 6, 5, 4, 2, 9, 3, 12];
2) Просеиваем элемент a[1], получаем: [11, 10, 8, 7, 9, 6, 5, 4, 2, 1, 3, 12];
3) Меняем местами a[1] и a[11]: [3, 10, 8, 7, 9, 6, 5, 4, 2, 1, 11, 12];
4) Просеиваем a[1], получаем: [10, 9, 8, 7, 3, 6, 5, 4, 2, 1, 11, 12];
5) Меняем местами a[1] и a[10]: [1, 9, 8, 7, 3, 6, 5, 4, 2, 10, 11, 12];
6) Просеиваем элемент a[1]: [9, 7, 8, 4, 3, 6, 5, 1, 2, 10, 11, 12];
7) Меняем местами a[1] и a[9]: [2, 7, 8, 4, 3, 6, 5, 1, 9, 10, 11, 12];
8) Просеиваем элемент a[1]: [8, 7, 6, 4, 3, 2, 5, 1, 9, 10, 11, 12];
9) Меняем местами a[1] и a[8]: [1, 7, 6, 4, 3, 2, 5, 8, 9, 10, 11, 12];
10) Просеиваем элемент a[1]: [7, 4, 6, 1, 3, 2, 5, 8, 9, 10, 11, 12];
11) Меняем местами a[1] и a[7]: [5, 4, 6, 1, 3, 2, 7, 8, 9, 10, 11, 12];
12) Просеиваем элемент a[1]: [6, 4, 5, 1, 3, 2, 7, 8, 9, 10, 11, 12];
13) Меняем местами a[1] и a[6]: [2, 4, 5, 1, 3, 6, 7, 8, 9, 10, 11, 12];
14) Просеиваем элемент a[1]: [5, 4, 2, 1, 3, 6, 7, 8, 9, 10, 11, 12];
15) Меняем местами a[1] и a[5]: [3, 4, 2, 1, 5, 6, 7, 8, 9, 10, 11, 12];
16) Просеиваем элемент a[1]: [4, 3, 2, 1, 5, 6, 7, 8, 9, 10, 11, 12];
17) Меняем местами a[1] и a[4]: [1, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12];
18) Просеиваем элемент a[1]: [3, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12];
19) Меняем местами a[1] и a[3]: [2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
20) Просеивать уже ничего не нужно;
21) Меняем местами a[1] и a[2]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
22) Просеивать ничего не нужно, сортировка закончена.
Эффективность алгоритма пирамидальной сортировки
Пирамидальная сортировка хорошо работает с большими массивами,
однако на маленьких примерах (N < 20) выгода от её применения может быть
не слишком очевидна.
В среднем этот алгоритм имеет сложность, пропорциональную N*log N.
Задание на практическое занятие №6
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа № 7. Сортировка массивов.
Методические указания к выполнению практического задания № 7
Пусть задан массив a целых чисел длины n. Первый способ сортировки
называется сортировкой обменом. Идея алгоритма достаточно проста. Найдем
среди элементов массива минимальный и поменяем его с первым элементом,
затем найдем минимальный среди оставшихся и поменяем его со вторым
элементом и т.д. Фрагмент программы на Паскале выглядит так:
{Для всех элементов массива, кроме последнего, сделать}
for j:=1 to n-1 do
begin
m := j; {Ищем минимальный элемент (вначале j-й) }
for k:=j+1 to n do
if a[k]<a[m] then
m:=k; {Текущий минимальный элемент - k-й}
x:=a[j]; {Обменять минимальный элемент с j-ым}
a[j]:=a[m];
a[m]:=x;
end;
Второй способ сортировки называется пузырьковым. В первом цикле,
начиная с первого, сравниваются соседние элементы массива, и они
переставляются, если последующий элемент меньше предыдущего. В конце
цикла самый большой элемент автоматически оказывается в конце массива (
всплывает в конец массива, отсюда название пузырьковый ). Во втором цикле
то же самое проделывается с элементами массива, исключая последний, и т.д.
Фрагмент программы на Паскале выглядит так:
{Для всех начальных частей массива сделать}
for j:=n downto 2 do
{Сравнить все пары соседних элементов от первого до (j-1)-го}
for k:=1 to j-1 do
if a[k]>a[k+1]
begin x:=a[k]; {Обменять k-й элемент с (k+1)-ым}
a[k]:=a[k+1];
a[k+1]:=x;
end;
Третий способ сортировки называется сортировкой слиянием. Пусть
массив a длины n и массив b длины m уже отсортированы в возрастающем
порядке, и пусть мы хоти получить массив длины (n+m), состоящий из их
объединения и также отсортированный. Это делается за один проход по
массиву a и массиву b следующим образом:
j:=1; k:=1; i:=1;
{Текущими являются первые элементы массивов a, b и c}
{Пока не кончатся оба массива}
while (j<=n) or (k<=m) do
begin
if (j<=n) and ((k>m) or (a[j]<=b[k])) then
{Массив a не кончился, а массив b кончился, либо массив b не кончился и
текущий элемент массива a меньше или равен текущему элементу массива b}
begin
c[i]:=a[j]; {Записываем в массив c элемент массива a}
j:=j+1; {Сдвигаем текущий элемент массива a}
l:=i+1; {Сдвигаем текущий элемент массива c}
end
else
if (k<=m) and ((j>n) or (a[j]>b[k])) then
{Массив b не кончился, а массив a кончился, либо массив a не кончился и
текущий элемент массива a больше текущего элемента массива b}
begin
c[i]:=b[k]; {Записываем в массив c элемент массива b}
k:=k+1; {Сдвигаем текущий элемент массива b}
l:=i+1; {Сдвигаем текущий элемент массива a}
end;
end;
Идея полного алгоритма сортировки массива слиянием заключается в
следующем. Предположим для простоты, что длина массива равна степени
двойки. Разобьем весь массив на пары соседних элементов и упорядочим
каждую пару по возрастанию. Затем каждые две соседних пары сольем в одну
упорядоченную четверку. В следующем цикле сольем каждые две соседних
четверки в одну упорядоченную восьмерку и т.д. Для такого алгоритма
необходима модификация описанной выше процедуры слияния, которая
сливала бы два следующих друг за другом фрагмента одного массива и
записывала результат в другой массив. Если длина массива не равна степени
двойки, то сливаемые фрагменты массива могут иметь разную длину, но
принципиально ничего не изменится.
Преимущество сортировки слиянием заключается в том, что один цикл
слияния происходит за один проход всего массива и поэтому таким методом
можно сливать не только массивы, но и большие файлы, списывая их
фрагментами в массивы в оперативную память. Алгоритм при этом несколько
усложняется, но принципиально остается тем же. Количество циклов при
сортировке слиянием (число проходов по всему массиву или файлу) равно
приблизительно log2n, где n - длина массива или файла.
Принципиально иной алгоритм сортировки основан на идее упорядочения
массива по частям. Пусть m и M - минимальное и максимальное значения
элементов массива. Положим b=(m+M)/2. Переставим элементы массива так,
чтобы в начале шли ( в произвольном порядке) элементы, меньшие или равные
b, а затем элементы большие b. Для этого будем двигаться одновременно с
начала массива до элемента, большего b, и с конца массива до элемента,
меньшего b. Как только мы их найдем, переставим эти элементы местами и
начнем двигаться дальше. Процесс заканчивается тогда, когда мы встретимся
где-то в середине массива. Соответствующий фрагмент программы на Паскале
записывается так:
j:=1; k:=n; {Текущими являются первый и последний элементы массива}
while j<k do{Пока мы не встретимся в середине массива}
begin
while (j<k) and (a[j]<=b) do j:=j+1;
{Дойти до номера j, для которого a[j]>b}
while (j<k) and (a[k]>b) do k:=k-1;
{Дойти до номера k, для которого a[k]<=b}
if j<k then begin x:=a[j];
{Поменять a[j] и a[k]}
a[j]:=a[k];
a[k]:=x;
end;
end;
После описанной процедуры осталось отсортировать отдельно первую
часть массива (до конечного значения переменных j и k) и вторую часть
массива. Для этого аналогичную процедуру нужно повторить для первой части
массива и значения b1=(b+m)/2 и второй части массива и значения b2=(b+M)/2.
Для полученных двух кусков первой и второй частей повторить то же самое и
т.д. Процесс прекращается тогда, когда при делении образуются фрагменты
массива длины 1. Конечно, процедуру нужно модифицировать таким образом,
чтобы она умела разделять произвольные фрагменты массива с произвольным
пороговым значением b.
Последний алгоритм, который мы здесь рассмотрим - это сортировка
деревом. Предположим, что элементы массива размещены в вершинах
бинарного дерева, то есть вершинами дерева будут номера элементов от 1 до n).
Будем считать, что элемент a[k] подчиняет элементы a[2k] и a[2k+1] (если 2k+1
или 2k больше n, соответствующие стрелки в дереве отсутствуют). корнем
дерева является элемент a[1]. Постараемся перестановками элементов добиться
того, чтобы каждый отец был не меньше своих сыновей. Назовем такое дерево
регулярным. Очевидно, максимальным в регулярном дереве является корень
a[1]. Поменяем теперь элементы a[1] и a[n] и будем рассматривать оставшуюся
часть массива длины n-1. Эта часть не будет регулярным деревом, так как оно
испорчено: одна из вершин (с номером n) выкинута, а ее значение перенесено в
корень. Однако испорчено оно несильно и его можно быстро исправить: корень
дерева нужно поменять местами с максимальным из его сыновей, этого сына - с
максимальным из его сыновей и т.д., пока очередная вершина не окажется
больше своих сыновей либо их не будет вовсе. После этого в корне снова
окажется максимальный элемент из оставшихся (n-1)-го, и его следует
поменять с элементом a[n-1]. Производя подобную процедуру со все меньшими
фрагментами массива, мы добьемся того, что массив будет весь отсортирован.
Приведем вспомогательную процедуру восстановления регулярности
дерева в корне (k - длина еще не отсортированной части массива):
j := 1; {дерево регулярно везде, кроме, возможно, вершины с номером j}
while ((2*j+1<= k) and (a[2*j+1] > a[j])) or ((2*j <= k) and (a[2*j] > a[j])) do
if (2*j+1 <= k) and (a[2*j+1]>=a[2*j]) {Больше элемент a[2j+1]}
then begin x:=a[2*j+1]; {обменять местами a[j] и a[2j+1] }
a[2*j+1]:=a[j];
a[j] := x;
j := 2*j+1 {текущей будет вершина с номером 2j+1}
end
else begin x:=a[2*j]; {обменять местами a[j] и a[2j] }
a[2*j]:=a[j];
a[j] := x;
j := 2*j {текущей будет вершина с номером 2j}
end;
Возможно вместо указанного алгоритма использовать рекурсивную
процедуру:
procedure regul (j,k: integer); {Восстановить регулярность поддерева с
корнем j в массиве длины k}
var i: integer;
begin
if 2*j> k then i:=0 {Вершина j не имеет подчиненных}
else if 2*j= k then i:=2*j {Вершина j имеет одну подчиненную вершину 2j}
else {Вершина j имеет две подчиненные вершины 2j и 2j+1}
if a[2*j+1]>=a[2*j] {Выбрать максимальную из двух подчиненных вершин}
then i:=2*j+1
else i:= 2*j;
if (i>0) and (a[j]<a[i]) then {Вершина j не регулярная}
begin x := a[i]; {обменять местами a[j] и a[i] }
a[i] := a[j];
a[j] := x;
regul (i,k) {Рекурсивный вызов процедуры regul для того, чтобы восстановить
регулярность поддерева с корнем в вершине с номером i}
end;
end;
Аналогичная процедура используется для того, чтобы сделать весь массив
регулярным на начальной стадии сортировки. Мы не будем ее здесь приводить.
Задание на практическое занятие №7
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
Практическая работа № 8. Сортировка файлов.
Методические указания к выполнению практического задания № 8
Дисковые файлы с произвольным доступом, которые используются
в большинстве баз данных на микро-ЭВМ, обладают двумя основными
преимуществами над последовательными дисковыми
вых,
файлами.
Во-пер-
их легко поддерживать. Обновление информации может произво-
диться без копирования всего списка. Во-вторых, их можно рассматривать
как
большие
массивы,
расположенные
на
диске,
что
в
значительной мере упрощает сортировку.
Последнее
преимущество
позволяет
использовать
алгоритм
рой сортировки в некоторыми его модификациями для поиска
быстразлич-
ных записей на диске аналогично индексированию массива. В отличие
от
сортировки
файла
последовательного
файла
при
сортировке
дискового
с произвольным доступом не требуется иметь на диске прост-
ранство одновременно как для отсортированного, так и для неотсортированного массива.
В каждом конкретном случае алгоритм сортировки должен модифицироваться
в
соответствии
со структурой сортируемых данных и
выбранным ключом сортировки. Однако основные принципы сортировки
дисковых файлов произвольного доступа можно понять,
изучая прог-
рамму сортировки записей с адресами почтовых корреспонденций /запись
"address" определялась раньше/.
предполагается,
В приводимом ниже примере,
что число элементов фиксированно и равно восьми-
десяти. На практике счетчик записей должен поддерживаться динамически
{ пример программы сортировки списка почтовых адресов }
programm MlistSort;
type
address = record
name: string[30];
street: string[40];
sity: string[20];
state: string[2];
zip: string[9];
end;
str80 = string[80];
DataItem = addres;
DataArray = array [1..80] of DataItem
recfil = file of DataItem
var
test: DataItem;
t, t2:integer;
testfile: recfil;
{ найти запись в файле }
function Find(var fp:recfil; i:integer): str80
var
t:address;
begin
i := i-1;
Seek(fp, i)
Read(fp, t)
Find := t.name;
end;
procedure QsRand(var var fp:recfil; count:integer)
procedure Qs(l, r:integer)
var
i, j, s:integer ;
x, y, z:DataItem;
begin
i := l; j := r;
s := (l+r) div 2;
Seek(fp,s-1); { получить запись }
Reed(fp,x);
repeat
while Find(fp, i) < x.name do i := i+1;
while x.name < Find(fp, j) do j := j-1;
if i<=j then
begin
Seek(fp,i-1); Reed(fp,y);
Seek(fp,j-1); Reed(fp,z);
Seek(fp,j-1); Write(fp,y);
Seek(fp,i-1); Write(fp,z);
i := i+1; j := j-1;
end;
until i>y;
if l<j then qs(l, j)
if l<r then qs(i, r)
end;
begin
qs(1,count);
end; { конец быстрой сортировки файла произвольного
доступа }
begin
Assign(testfile, 'rectest.dat');
Reset(testfile);
t := 1;
while not EOF(testfile) do begin
Read(testfile,test); { подсчет числа записей в
файле}
t := t+1;
end;
t := t-1;
QsRand(testfile,t)
end.
Функция Find используется для того,
ном
неизменным
программу
чтобы оставить в основ-
быстрой сортировки.
Результатом этой
функции является символьная строка "name" из записи,
расположен-
ной на диске. Необходимо постоянно вычитать единицу из аргументов
функций Seek и Find,
так как записи дискового
файла
нумеруются
начиная с нуля, а массивы нумеруются с единицы.
В отличие от файлов с прямым доступом последовательные файлы
обычно
не
используют
записи фиксированной длины и могут разме-
щаться на устройствах памяти, на которых трудно организовать прямой доступ. Поэтому последовательные файлы на дисках используются
в тех случаях,
пользовать
когда для решения конкретной задачи
записи
ориентировано
текстовых
переменной
на
Несмотря на то,
ис-
длины или когда устройство памяти
последовательный
файлов
удобнее
имеют
доступ.
Например,
последовательную
большинство
организацию.
что такой подход к сортировке, когда диско-
вый файл рассматривается как массив,
имеет ряд преимуществ,
нельзя применить к последовательным файлам
-
быстрый
доступ
его
к
произвольному элементу в этом случае невозможен. Например, нельзя
быстро прочитать произвольную запись из
последовательного файла,
расположенного
этой причине возникают
на
магнитной
ленте.
По
трудности по применению любого ранее описанного метода сортировки
массивов
Имеется
к
два
сортировке
подхода
к
последовательных
сортировке
последовательных
файлов.
файлов.
Первый подход предусматривает считывание информации в оперативную
память и сортировку при помощи одного из стандартных методов сортировки массивов. Несмотря на то, что при таком подходе сортировка будет выполняться быстро,
вают
сильное
ограничение
размеры оперативной памяти накладына
размер
сортируемого
файла.
При использовании второго подхода, получившего название сортировки слиянием,
весь файл делиться на две равные части. В про-
цессе сортировки из каждого файла считывается по одному элементу,
эта
пара элементов упорядочивается и делается запись элементов в
третий файл,
расположенный на диске.
Новый файл затем снова де-
лится на два файла и упорядоченные пары элементов объединяются в
упорядоченные группы элементов по четыре элемента в каждой. Затем
полученный
файл
снова
разделяется на два файла и вся процедура
повторяется до тех пор, пока файл не будет отсортирован. Эту сортировку-слияние называют трехленточным слиянием, поскольку в этом
случае одновременно требуется иметь три файла /т.е. три накопителя
на
магнитной
Для
того,
ленте,
чтобы
если
лучше
файл
понять
располагается
работу
на
ленте/.
сортировки-слияние,
рассмотрим следующую последовательность числе:
1 4 3 8 6 7 2 5.
В результате разбиения получаться следующие последовательности:
1438
6 7 2 5.
Затем производится слияние пар элементов:
1 6 - 4 7 - 2 3 - 5 8.
Новое разбиение дает следующие последовательности:
16-47
2 3 - 5 8.
Результат следующего слияния:
1 2 3 6 - 4 5 7 8.
Последнее разбиение будет следующим:
1236
4 5 7 8.
И в результате получаем:
1 2 3 4 5 6 7 8.
При сортировке методом слияния,
ли,
требуется
как возможно вы уже замети-
выполнить log n операций доступа к каждому файлу,
где "n" является числом сортируемых элементов.
Ниже дается простая версия алгоритма сортировки методом слияния. Предполагается, что размер входного файла в два раза превышает объем содержащейся в нем информации. Поэтому в действтительности требуется иметь лишь один файл.
Однако, по-существу, метод
сортировки
"filtype"
слиянием
определяется
не
как
изменяется.
файл
типа
В
этом
"DataItem".
примере
данное
Функция
"Find"
используется для считывания конкретной записи из файла.
{ функция "Find" используется в сортировке методом
слияния для считывания из файла конкретной записи.}
function Find(var fp:filtype; i:integer):DataItem;
var
t:DataItem;
begin
Seek(fp, i-1);
Read(fp, t);
Find := t;
end;
procedure Mergesort(var fp: filetype; count:integer);
var
i, j, k, l, t, h, m, p, q, r: integer;
ch1, ch2:DataItem
up: Boolean;
begin
up := TRUE;
p := 1;
repeat
h := 1; m := count;
if up then
begin
i := 1; j := count; k := count+1; l := 2*count;
end else
begin
k := 1; l := count; i := count+1; j := 2*count;
end;
repeat
if m>=p then q := p else q := m;
m := m-q;
if m>=p then r := p else r := m;
m := m-r;
while (q<>0) and (r<>0) do
begin
if Find(fp,i) < Find(fp,j) then
begin
Seek(fp, i-1); Read(fp,ch2);
Seek(fp, k-1); Write(fp,ch2);
k := k+h; i := i+1; q := q-1;
end else
begin
Seek(fp, j-1); Read(fp,ch2);
Seek(fp, k-1); Write(fp,ch2);
k := k+h; j := j-1; r := r-1;
end;
end;
while r<>0 do
begin
Seek(fp, j-1); Read(fp,ch2);
Seek(fp, k-1); Write(fp,ch2);
k := k+h; j := j-1; r := r-1;
end;
while q<>0 do
begin
Seek(fp, i-1); Read(fp,ch2);
Seek(fp, k-1); Write(fp,ch2);
k := k+h; i := i+1; q := q-1;
end;
h := -1; t := k;
k := l;
l := t;
until m = 0:
up := not up;
p := p*2;
until p >= count;
if not up then
for i := 1 to count do
begin
Seek(fp, i-1+count); Read(fp,ch2);
Seek(fp, i-1); Write(fp,ch2);
end;
end; { кoнец сортировки методом слияния }
Задание на практическое занятие №8
1. Отработать данный метод сортировки данных указанным методом,
применяя свои данные, указанные по вариантам.
2. Распечатать листинг программы.
3. Провести защиту практической работы в устной форме
5 СРОП И СРО
При кредитной системе обучения предъявляются высокие требования к
повышению качества организации самостоятельной работы студента, которая
включает выполнение различных домашних заданий.
Самостоятельная работа студента под руководством преподавателя – одна
из форм учебной работы при кредитной системе обучения, которая проводится
в виде аудиторного занятия в диалоговом режиме, а также в виде консультаций
во внеаудиторное время.
Содержание самостоятельной работы студентов под руководством
преподавателя и чисто самостоятельной работы студента приведено в таблице 1
Таблица 1 – План СРОП и СРО
СРОП
СРО
№
п/п
1
2
3
1. Сортировка элементов в файле
Усовершенствованные
методы
(программа, отчет).
сортировки
массивов(программа,
отчет).
Сортировка последовательных
Введение в рекурсию(реферат).
файлов(программа, отчет).
Списковые структуры (реферат).
Динамические
структуры
данных(реферат).
Поиск и хранение данных(реферат). Методы
анализа
алгоритмов(реферат).
Деревья и графы(реферат).
Методы
поиска
элементов
в
массиве(программа, отчет).
Download