2.3.1 Описание алгоритма
Многие алгоритмы по природе своей рекурсивны (recursive): решая некоторую задачу, они вызывают самих себя для решения её подзадач. Идея метода «разделяй и властвуй» состоит как раз в этом. Сначала задача разбивается на несколько подзадач меньшего размера. Затем эти задачи решаются (с помощью рекурсивного вызова – или непосредственно, если размер достаточно мал). Наконец, их решения комбинируются и получается решение исходной задачи.
Для задачи сортировки эти три этапа выглядят так. Сначала массив разбивается на две половины меньшего размера. Затем каждая из половин сортируется отдельно. После этого остаётся соединить два упорядоченных массива половинного размера в один. Рекурсивное разбиение задачи на меньшие происходит до тех пор, пока размер массива не дойдёт до единицы (любой массив длины 1 можно считать упорядоченным).
Нетривиальным этапом является соединение двух упорядоченных массивов в один. Оно выполняется с помощью вспомогательной процедуры Merge(A,p,q,r). Параметрами этой процедуры являются массив А и числа р, q, r, указывающие границы сливаемых участков. Процедура предполагает, что p ≤ q < r и что участки А[р..q] и A[q + 1..r] уже отсортированы, и сливает (merges) их в один участок А[р..r]. Алгоритм данной процедуры очень прост – на начало каждого из участков устанавливаются два курсора – i и j. Процедура объединения потребует наличия дополнительной памяти, равной объему входа, и количества элементарных шагов, прямо пропорционального объему входа, т.к. на каждой итерации один из элементов будет помещаться в результирующий массив. Предположим, что исходные объединяемые массивы отсортированы по возрастанию. Процедура очень проста – на каждой из итераций производится соревнование между курсорами. Право на копирование своего элемента в целевой массив получает тот курсор, который в данный момент находится над наименьшим элементом. Если оба элемента под курсорами равны, выигрывает находящийся слева (i), иначе данная сортировка не будет устойчивой, т.е. сохраняющей исходный порядок на одинаковых ключах. При сортировке по убыванию приоритет в соревновании также остается за левым курсором i. Ясно, что каждый шаг требует ограниченного числа действий, и общее число действий есть Θ(n).
Рисунок 2.2 – Сортировка слиянием для массива А =
На листинге 2.1 представлен листинг процедуры сортировки слиянием
Merge-Sort(A, p, r), которая сортирует участок А[р..r] массива А, не меняя остальную часть массива. При р ≥ r участок содержит максимум один элемент, и тем самым уже отсортирован. В противном случае мы отыскиваем число q, которое делит участок на две примерно равные части А[р..q] (содержит элементов) и A[q + 1..r] содержит элементов. Здесь через обозначается целая часть х (наибольшее целое число, меньшее или равное х), а через – наименьшее целое число, большее или равное х. Иначе, можно расшифровать как «округление вниз» а как «округление вверх».
Листинг 2.1 – Процедура сортировки слиянием
Весь массив теперь можно отсортировать с помощью вызова
Merge-Sort(A, 1, length[A]). Если длина массива n = length[A] есть степень двойки, то в процессе сортировки произойдёт слияние пар элементов в отсортированные участки длины 2, затем слияние пар таких участков в отсортированные участки длины 4 и так далее до n (на последнем шаге соединяются два отсортированных участка длины n / 2).
procedure MergeSort(n: integer;
var A: array[1..n] of integer);
{Процедура сортировки слиянием}
var
i, j, k, t, s, Start1, Fin1, Fin2: integer;
B: array[1..n] of integer;
begin
k := 1; {Начальное значение длины участков}
while k < n do begin {пока участок не весь массив}
t := 0; {начало 1-й пары участков}
while t+k < n do begin {пока не все участки просмотрели}
{Определяем границы рассматриваемых участков}
Start1 := t+1; Fin1 := t+k; {Начало и конец 1-го участка}
if t+2*k > n then Fin2 := n
else Fin2 := t+2*k; {Конец 2-го участка}
i := Start1; {Начальное значение индекса в 1-м участке}
j := Fin1 + 1; {Начальное значение индекса в 2-м участке}
s := 1; {Начальное значение индекса в массиве B}
{Заполняем B элементами из двух участков}
while (i <= Fin1) and (j <= Fin2) do begin
{Сравниваем попарно элементы из двух участков}
if A[i] < A[j] then begin
{Вставляем элемент из 1-го участка}
B[s] := A[i];
i := i + 1;
end else begin
{Вставляем элемент из 2-го участка}
B[s] := A[j];
j := j + 1;
end;
s := s + 1;
end;
{Добавляем в массив B оставшиеся элементы из 1-го участка}
while (i <= Fin1) do begin
B[s] := A[i];
i := i + 1; s := s + 1;
end;
{Добавляем в массив B оставшиеся элементы из 2-го участка}
while (j <= Fin2) do begin
B[s] := A[j];
j := j + 1; s := s + 1;
end;
t := Fin2; {Переходим к следующей паре участков}
end;
k := k * 2; {Удваиваем значение длины участков}
{Сохраняем полученный промежуточный результат}
for s := 1 to t do A[s] := B[s];
end;
end;
Листинг 2.2 – Нерекурсивная процедура сортировки слиянием (неустойчивый вариант реализации)
- Министерство образования Российской Федерации
- Содержание
- 1.2 Скорость роста функций
- 1.3 Анализ алгоритмов; время работы в лучшем, худшем случаях и в среднем
- 1.4 Типы данных, структуры данных и абстрактные типы данных
- 1.5 Динамические множества
- 2 Алгоритмы сортировок
- 2.1 Понятие внутренней и внешней сортировки
- 2.2 Сортировка вставками
- 2.3 Сортировка слиянием
- 2.3.1 Описание алгоритма
- 2.3.2 Анализ времени работы алгоритмов «разделяй и властвуй»
- 2.3.2 Анализ времени работы сортировки слиянием через рекуррентное соотношение
- 2.3.3 Анализ времени работы сортировки слиянием через геометрическую интерпретацию
- 2.4 Пирамидальная сортировка
- 2.4.1 Введение в алгоритм
- 2.4.2 Сохранение основного свойства кучи
- 2.4.3 Построение кучи
- 2.5 Быстрая сортировка
- 2.5.1 Введение в алгоритм
- 2.5.2 Описание
- 2.5.3 Разбиение массива
- 2.5.4 Особенности работы быстрой сортировки
- 2.6 Особенности реализации алгоритмов сортировки; сортировка за линейное время
- 2.6.1 Введение
- 2.6.2 Разрешающее дерево сортировки сравнениями
- 2.7 Цифровая сортировка
- 2.8 Сортировка вычерпыванием
- 2.8.1 Описание алгоритма
- 2.8.2 Вероятностный анализ времени работы сортировки вычерпыванием
- 2.8.3 Анализ времени работы сортировки вычерпыванием через геометрическую интерпретацию
- 2.9 Сортировка подсчетом
- 2.9.1 Описание алгоритма
- 2.9.2 Анализ времени работы
- 3 Элементарные и нелинейные структуры данных
- 3.1 Элементарные структуры: список, стек, очередь, дек
- 3.1.1 Список Линейный однонаправленный список
- Линейный двунаправленный список
- Двунаправленный список с фиктивными элементами
- Циклические списки
- Циклический однонаправленный список
- Циклический двунаправленный список
- 3.1.2 Стек
- 3.1.3 Очередь
- 3.1.3 Дек
- 3.2 Нелинейные структуры данных
- 3.2.1 Представление корневых деревьев в эвм
- Обходы деревьев
- 3.2.2 Двоичные деревья Спецификация двоичных деревьев
- Реализация
- Обходы двоичных деревьев
- 3.2.3 Двоичные деревья поиска Основные операции
- Минимум и максимум
- Следующий и предыдущий элементы
- Добавление и удаление
- Случайные деревья поиска
- Оптимальные деревья поиска
- 4 Хеширование
- 4.1 Введение
- 4.2 Прямая адресация; таблицы с прямой адресацией
- 4.3 Хеш – таблицы; возникновение коллизий и их разрешение
- Разрешение коллизий с помощью цепочек
- Анализ хеширования с цепочками
- 4.4 Способы построения хеш – функций Выбор хорошей хеш-функции
- Ключи как натуральные числа
- Деление с остатком
- Умножение
- Универсальное хеширование
- 4.5 Открытая адресация; способы вычисления последовательности испробованных мест: линейная последовательность проб, квадратичная последовательность проб, двойное хеширование
- Линейная последовательность проб
- 1 / (1 – )
- 5 Основные принципы разработки алгоритмов
- 5.1 Введение в теорию графов
- 5.1.1 Графы
- 5.1.2 Представление графов
- 5.2 Алгоритмы на графах: поиск в ширину, поиск в глубину
- 5.2.1 Поиск в ширину (волновой алгоритм)
- 5.2.2 Анализ поиска в ширину
- 5.2.3 Деревья поиска в ширину
- 5.2.4 Поиск в глубину
- 5.2.5 Анализ поиска в глубину
- 5.2.6 Свойства поиска в глубину
- 5.2.7 Классификация рёбер
- 5.3 Топологическая сортировка, задача о разбиении графа на сильно связанные компоненты
- 5.3.1 Топологическая сортировка
- 5.3.2 Сильно связные компоненты
- 5.4 Алгоритм построения минимального остовного дерева
- 5.4.1 Остовные деревья минимальной стоимости
- 5.4.2 Построение минимального покрывающего дерева
- 5.4.3 Алгоритмы Крускала и Пpимa
- 5.4.4 Алгоритм Крускала
- 5.4.5 Алгоритм Прима
- 5.5 Задача нахождения кратчайших путей на графах; алгоритм Флойда; алгоритм Дейкстры
- 5.5.1 Нахождение кратчайшего пути
- 5.5.2 Алгоритм Дейкстры
- 5.5.3 Алгоритм Флойда
- 5.6 Поиск с возвратом
- 5.6.1 Введение
- 5.6.2 Переборные алгоритмы
- 5.6.3 Метод ветвей и границ
- 5.6.4 Метод альфа-бета отсечения
- 5.6.5 Локальные и глобальные оптимальные решения
- 5.7 Метод декомпозиции ( «Разделяй и властвуй»)
- 5.7.1 «Ханойские башни»
- 5.8 Жадные алгоритмы и динамическое программирование
- 5.8.1 Задача о выборе заявок
- 5.8.2 Дискретная задача о рюкзаке
- 5.8.3 Непрерывная задача о рюкзаке
- 5.8.4 Числа Фибоначчи
- 5.8.5 Задача триангуляции многоугольника
- 5.8.6 Дп, жадный алгоритм или что-то другое?