- •Введение
- •Содержание и структура пособия
- •Задачи поиска в информатике
- •Задача поиска в таблице
- •Поиск в массивах
- •Линейный поиск
- •Бинарный поиск
- •Вопросы и упражнения
- •Алгоритмы сортировки массивов
- •Задачи сортировки таблиц и массивов
- •Простые алгоритмы сортировки
- •Алгоритм пузырька (BubbleSort)
- •Алгоритм выбора (SelectionSort)
- •Алгоритм вставок (InsertionSort)
- •Общая характеристика простых алгоритмов
- •Алгоритм Шелла (ShellSort)
- •Алгоритм быстрой сортировки (QuickSort)
- •Алгоритм пирамидальной сортировки (HeapSort)
- •Алгоритмы сортировки слиянием (MergeSort)
- •Задача внешней сортировки
- •Сравнение алгоритмов сортировки
- •Вопросы и упражнения
- •Сортировка и поиск с использованием деревьев
- •Задача поиска со вставкой
- •Бинарные деревья поиска
- •Var t: Tree; {Кореньдерева}
- •X: KeyType; {Искомый ключ}
- •Var found: Boolean {Найдено или вставлено?}
- •Страничные деревья поиска
- •Использование b-деревьев в базах данных
- •Вопросы и упражнения
- •Специальные методы сортировки и поиска
- •Поразрядная сортировка
- •Поиск в словаре и нагруженные деревья
- •Вопросы и упражнения
- •Хеширование
- •Основные понятия хеширования
- •Способы построения хеш-функций
- •Общие требования к хеш-функциям
- •Алгоритм деления
- •Алгоритм середины квадрата
- •Алгоритм умножения
- •Хеширование строковых ключей
- •Методы разрешения коллизий
- •Алгоритм линейных проб
- •Алгоритм квадратичных проб
- •Алгоритм двойного хеширования
- •Алгоритм списков в динамической памяти
- •Алгоритм Уильямса
- •Эффективность хеширования и сравнение с другими методами поиска
- •Вопросы и упражнения
- •Дополнительные вопросы поиска
- •Определение порядковых статистик
- •Задача поиска в строке
- •Постановка задачи и «прямое» решение
- •Алгоритм Кнута, Морриса и Пратта
- •Алгоритм Бойера – Мура
- •Вопросы и упражнения
- •Заключение
- •Библиографический список
Алгоритм Шелла (ShellSort)
Идея этого алгоритма в следующем. Разобьем элементы сортируемого массива на hцепочек, каждая из которых состоит из элементов, отстоящих друг от друга на расстояниеh(здесьh– произвольное натуральное число). Первая цепочка будет содержать элементыa1,ah+1,a2h+1,a3h+1и т.д., вторая –a2,ah+2,a2h+2и т.д., последняя цепочка –ah,a2h,a3hи т.д. Отсортируем каждую цепочку как отдельный массив, используя для этого метод простых или бинарных вставок. Затем выполним все вышеописанное для ряда убывающих значенийh, причем последний раз – дляh = 1.
Очевидно, массив после этого окажется сортированным. Неочевидно, что все проходы при h > 1не были пустой тратой времени. Тем не менее, оказывается, что дальние переносы элементов при большихhнастолько приближают массив к сортированному состоянию, что на последний проход остается очень мало работы.
Большое значение для эффективности алгоритма Шелла имеет удачный выбор убывающей последовательности значений h. Казалось бы, самая естественная убывающая последовательность чисел это степени двойки: …, 64, 32, 16, 8, 4, 2, 1. Сам автор алгоритма Дональд Шелл первоначально предложил использовать именно эти числа. На самом же деле выбор этой последовательности – один из самых неудачных! Пусть, например, в исходном массиве все нечетные по номеру элементы отрицательны, а четные – положительны. Как будет выглядеть такой массив после выполнения всех проходов, кроме последнего (сh= 1)? Поскольку все использованныеhбыли четными, ни один элемент не мог переместиться с четного места на нечетное и наоборот. Таким образом, все нечетные элементы по-прежнему будут отрицательны (хотя и отсортированы между собой), а четные – положительны. Подобный массив трудно назвать «почти сортированным»! На последний проход алгоритма вставки остается слишком много работы.
Чтобы избежать подобной неприятности, желательно, чтобы при соседних значениях kзначенияhkне были кратны друг другу. В литературе обычно рекомендуется использовать одну из двух последовательностей:hk+1=3hk+1илиhk+1=2hk+1. В обоих случаях в качестве начальногоhkвыбирается такое значение из последовательности, при котором все сортируемые цепочки имеют длину не меньше 2. Чтобы воспользоваться, например, первой из этих формул, надо сначала положитьh1:=1, а затем в цикле увеличивать значениеhпо формулеhk+1 := 3*hk+1, пока для очередногоhkне будет выполнено неравенствоhk (n–1) div 3. Это значениеhkследует использовать на первом проходе алгоритма, а затем можно получать следующие значения по обратной формуле:hk–1 := (hk–1) div 3, вплоть доh1=1.
Для каждой из этих двух последовательностей время работы в худшем случае оценивается как Tмакс(n) = O(n3/2). Это значительно лучше, чемO(n2)для простых алгоритмов. Для среднего времени работы известны только эмпирические оценки, показывающие, что время растет примерно какO(n1.25)илиO(n1.3).
Ряд авторов на основе теоретического анализа сортировки Шелла предложили использовать более сложные последовательности, повышающие эффективность алгоритма. Одной из лучших считается последовательность, предложенная Р.Седжвиком:
Доказано, что при выборе hkпо Седжвику максимальное время работы алгоритма естьO(n4/3). Среднее время по эмпирическим оценкам равно примерноO(n7/6).
На практике удобно раз и навсегда вычислить достаточное количество членов последовательности Седжвика (вот они: 1, 5, 19, 41, 109, 209, 505, 929, 2 161, 3 905, 8 929, 16 001, 36 289, 64 769, 14 6305, 260 609, 587 521, 1 045 505, ...), затем по заданномуnвыбрать такоеk, при которомhk >= (n–1) div 3, а далее в цикле выбирать значения последовательностиhkпо убываниюk.