
- •Что я должен предварительно знать?
- •Какая версия Delphi мне нужна?
- •Что и где я могу найти в книге, или, другими словами, из чего состоит эта книга?
- •Глава 11 сконцентрирована вокруг нескольких технологий сжатия. Подробно рассматриваются такие алгоритмы сжатия, как Шеннона‑Фано, Хаффмана, с применением скошенного дерева и lz77.
- •От изготовителя fb2.
- •Благодарности
- •Глава 1. Что такое алгоритм?
- •Что такое алгоритм?
- •Анализ алгоритмов
- •О‑нотация
- •Лучший, средний и худший случаи
- •Алгоритмы и платформы
- •Виртуальная память и страничная организация памяти
- •Пробуксовка
- •Локальность ссылок
- •Кэш процессора
- •Выравнивание данных
- •Пространство или время
- •Длинные строки
- •Использование ключевого слова const
- •Осторожность в отношении автоматического преобразования типов
- •Тестирование и отладка
- •Утверждения
- •Комментарии
- •Протоколирование
- •Трассировка
- •Анализ покрытия
- •Тестирование модулей
- •Отладка
- •Глава 2. Массивы.
- •Массивы
- •Типы массивов в Delphi
- •Стандартные массивы
- •Динамические массивы
- •Новые динамические массивы
- •Класс tList, массив указателей
- •Краткий обзор класса tList
- •Класс TtdObjectList
- •Массивы на диске
- •Глава 3. Связные списки, стеки и очереди
- •Односвязные списки
- •Узлы связного списка
- •Создание односвязного списка
- •Вставка и удаление элементов в односвязном списке
- •Соображения по поводу эффективности
- •Использование начального узла
- •Использование диспетчера узлов
- •Класс односвязного списка
- •Двухсвязные списки
- •Вставка и удаление элементов в двухсвязном списке
- •Использование начального и конечного узлов
- •Использование диспетчера узлов
- •Класс двухсвязного списка
- •Достоинства и недостатки связных списков
- •Стеки на основе односвязных списков
- •Стеки на основе массивов
- •Пример использования стека
- •Очереди
- •Очереди на основе односвязных списков
- •Очереди на основе массивов
- •Глава 4. Поиск.
- •Процедуры сравнения
- •Последовательный поиск
- •Массивы
- •Связные списки
- •Бинарный поиск
- •Массивы
- •Связные списки
- •Вставка элемента в отсортированный контейнер
- •Глава 5. Сортировка
- •Алгоритмы сортировки
- •Тасование массива tList
- •Основы сортировки
- •Самые медленные алгоритмы сортировки
- •Пузырьковая сортировка
- •Шейкер‑сортировка
- •Сортировка методом выбора
- •Сортировка методом вставок
- •Быстрые алгоритмы сортировки
- •Сортировка методом Шелла
- •Сортировка методом прочесывания
- •Самые быстрые алгоритмы сортировки
- •Сортировка слиянием
- •Быстрая сортировка
- •Сортировка слиянием для связных списков
- •Глава 6. Рандомизированные алгоритмы.
- •Генерация случайных чисел
- •Критерий хи‑квадрат
- •Метод средних квадратов
- •Линейный конгруэнтный метод
- •Тестирование
- •Тест на однородность
- •Тест на пропуски
- •Тест "покер"
- •Тест "сбор купонов"
- •Результаты выполнения тестов
- •Комбинирование генераторов
- •Аддитивные генераторы
- •Тасующие генераторы
- •Выводы по алгоритмам генерации случайных чисел
- •Другие распределения случайных чисел
- •Списки с пропусками
- •Поиск в списке с пропусками
- •Вставка в список с пропусками
- •Удаление из списка с пропусками
- •Полная реализация класса связного списка
- •Глава 7. Хеширование и хеш‑таблицы
- •Функции хеширования
- •Простая функция хеширования для строк
- •Функции хеширования pjw
- •Разрешение конфликтов посредством линейного зондирования
- •Преимущества и недостатки линейного зондирования
- •Удаление элементов из хеш‑таблицы с линейным зондированием
- •Класс хеш‑таблиц с линейным зондированием
- •Другие схемы открытой адресации
- •Квадратичное зондирование
- •Псевдослучайное зондирование
- •Двойное хеширование
- •Разрешение конфликтов посредством связывания
- •Преимущества и недостатки связывания
- •Класс связных хеш‑таблиц
- •Разрешение конфликтов посредством группирования
- •Хеш‑таблицы на диске
- •Расширяемое хеширование
- •Глава 8. Бинарные деревья.
- •Создание бинарного дерева
- •Вставка и удаление с использованием бинарного дерева
- •Перемещение по бинарному дереву
- •Обход в ширину, симметричный обход и обход в глубину
- •Обход по уровням
- •Реализация класса бинарных деревьев
- •Деревья бинарного поиска
- •Вставка в дереве бинарного поиска
- •Удаление из дерева бинарного поиска
- •Реализация класса дерева бинарного поиска
- •Перекомпоновка дерева бинарного поиска
- •Скошенные деревья
- •Реализация класса скошенного дерева
- •Красно‑черные деревья
- •Вставка в красно‑черное дерево
- •Удаление из красно‑черного дерева
- •Глава 9. Очереди по приоритету и пирамидальная сортировка.
- •Очередь по приоритету
- •Первая простая реализация
- •Вторая простая реализация
- •Сортирующее дерево
- •Вставка в сортирующее дерево
- •Удаление из сортирующего дерева
- •Реализация очереди по приоритету при помощи сортирующего дерева
- •Пирамидальная сортировка
- •Алгоритм Флойда
- •Завершение пирамидальной сортировки
- •Расширение очереди по приоритету
- •Восстановление свойства пирамидальное
- •Отыскание произвольного элемента в сортирующем дереве
- •Реализация расширенной очереди по приоритету
- •Глава 10. Конечные автоматы и регулярные выражения.
- •Конечные автоматы
- •Использование конечного автомата: синтаксический анализ
- •Синтаксический анализ файлов с разделяющими запятыми
- •Детерминированные и недетерминированные конечные автоматы
- •Регулярные выражения
- •Использование регулярных выражений
- •Синтаксический анализ регулярных выражений
- •Компиляция регулярных выражений
- •Сопоставление строк с регулярными выражениями
- •Глава 11. Сжатие данных.
- •Представление данных
- •Сжатие данных
- •Типы сжатия
- •Потоки битов
- •Сжатие с минимальной избыточностью
- •Кодирование Шеннона‑Фано
- •Кодирование Хаффмана
- •Кодирование с использованием скошенного дерева
- •Сжатие с использованием словаря
- •Описание сжатия lz77
- •Особенности кодирования литеральных символов и пар расстояние/длина
- •Восстановление с применением алгоритма lz77
- •Сжатие lz77
- •Глава 12. Дополнительные темы.
- •Алгоритм считывания‑записи
- •Алгоритм производителей‑потребителей
- •Модель с одним производителем и одним потребителем
- •Модель с одним производителем и несколькими потребителями
- •Поиск различий между двумя файлами
- •Вычисление lcs двух строк
- •Вычисление lcs двух файлов
- •Список литературы
Шейкер‑сортировка
Пузырьковая сортировка имеет одну малоизвестную вариацию, которая на практике дает незначительное увеличение скорости, ‑ это так называемая шейкер‑сортировка (shaker sort).
Рисунок 5.2. Два прохода с помощью шейкер‑сортировки
Вернемся к картам. Выполните первый проход согласно алгоритму сортировки. Туз попадет на первую позицию. Теперь, вместо прохода колоды карт справа налево, пройдите слева направо: сравните вторую и третью карты и старшую карту поместите на третью позицию. Сравните третью и четвертую карты, и при необходимости поменяйте их местами. Продолжайте сравнения вплоть до достижения пары (12, 13). По пути к правому краю колоды вы "захватили" короля и переместили его на последнюю позицию.
А теперь снова пройдите колоду справа налево до второй карты. Во вторую позицию попадет двойка. Продолжайте чередовать направления проходов до тех пор, пока не будет отсортирована вся колода.
Листинг 5.5. Шейкер‑сортировка
procedure TDShakerSort(aList :TList;
aFirst : integer; aLast : integer;
aCompare : TtdCompareFunc);
var
i : integer;
Temp : pointer;
begin
TDValidateListRange(aList, aFirst, aLast, 'TDShakerSort');
while (aFirst < aLast) do
begin
for i := aLast downto succ(aFirst) do
if (aCompare(aList.List^[i], aList.List^[i‑1]) < 0) then begin
Temp := aList.List^[i];
aList.List^[i] := aList.List^[i‑1];
aList.List^[i‑1] := Temp;
end;
inc(aFirst);
for i := succ(aFirst) to aLast do
if (aCompare(aList.List^[i], aList.List^[i‑1]) < 0) then begin
Temp := aList.List^[i];
aList.List^[i] := aList.List^[i‑1];
aList.List^[i‑1] := Teilend;
dec(aLast);
end;
end;
Несмотря на то что шейкер‑сортировка принадлежит к алгоритмам класса O(n(^2^)), время ее выполнения немного меньше, чем для пузырьковой сортировки. Причина, по которой алгоритм назван именно шейкер‑сортировкой, состоит в том, что элементы в списке колеблются относительно своих позиций до тех пор, пока список не будет отсортирован.
Как и пузырьковая сортировка, шейкер‑сортировка относится к неустойчивым алгоритмам.
Сортировка методом выбора
Следующим алгоритмом, который мы рассмотрим, будет сортировка методом выбора (selection sort). Это пока что первый метод, который действительно можно использовать в повседневной практике (о пузырьковой сортировке и шейкер‑сортировке можно уже забыть).
Начиная с правого края колоды, просмотрите все карты и найдите самую младшую (конечно, это будет туз). Поменяйте местами туз с первой картой. Теперь, игнорируя первую карту, снова просмотрите всю колоду справа налево в поисках самой младшей карты. Поменяйте местами младшую карту со второй картой. Далее, игнорируя первые две карты, просмотрите всю колоду справа налево в поисках самой младшей карты и поменяйте найденную карту с третьей картой. Продолжайте процесс до тех пор, пока вся колода не будет отсортирована. Очевидно, что тринадцатый цикл не понадобится, поскольку он будет манипулировать только с одной картой, которая к тому времени уже будет находиться в требуемой позиции.
Листинг 5.6. Сортировка методом выбора
procedure TDSelectionSort(aList : TList;
aFirst : integer; aLast : integer;
aCompare : TtdCompareFunc);
var
i, j : integer;
IndexOfMin : integer;
Temp : pointer;
begin
TDValidateListRange(aList, aFirst, aLast, 'TDSelectionSort');
for i := aFirst to pred(aLast) do
begin
IndexOfMin := i;
for j := succ(i) to aLast do
if (aCompare(aList.List^[j], aList.List^[IndexOfMin]) < 0) then
IndexOfMin := j;
if (aIndexOfMin <> i) then begin
Temp := aList.List^[i];
aList.List^[i] := aList.List^[IndexOfMin];
aList.List^[IndexOfMin] := Teilend;
end;
end;
Рисунок 5.3 Сортировка методом выбора
Как видите, в приведенном коде снова присутствуют два вложенных цикла, следовательно, сортировка методом выбора относится к алгоритмам класса O(n(^2^)). В первом цикле индекс проходит значения от aFast до aLast‑1 и при каждом его выполнении во внутреннем цикле определяется элемент с минимальным значением в оставшейся части списка. В отличие от нашего примера с картами, внутренний цикл заранее не знает, каковым будет минимальный элемент в списке, поэтому ему нужно просмотреть все элементы. После обнаружения минимального элемента он переставляется в требуемую позицию.
Сортировка методом выбора интересна одной своей особенностью. Количество выполняемых сравнений для первого прохода равно n, для второго ‑ n‑1 и т.д. Общее количество сравнений будет равно n (n + 1)/2 = 1, т.е. сортировка принадлежит к классу алгоритмов O(n(^2^)). Тем не менее, количество перестановок намного меньше: при каждом выполнении внешнего цикла производится всего одна перестановка. Таким образом, общее количество перестановок (n ‑ 1), т.е. O(n). Что это означает на практике? Если стоимость перестановки элементов намного больше, чем время сравнения (под стоимостью в данном случае понимается время или требуемые ресурсы), сортировка методом выбора оказывается достаточно эффективной.
Сортировка методом выбора относится к группе устойчивых алгоритмов. Поиск наименьшего значения будет возвращать первое в списке наименьшее значение из нескольких имеющихся. Таким образом, равные значения будут находиться в отсортированном списке в том же порядке, в котором они были в исходном списке.