
- •Что я должен предварительно знать?
- •Какая версия 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 двух файлов
- •Список литературы
Первая простая реализация
При разработке очереди по приоритету первый атрибут (возможность хранения произвольного количества элементов) наталкивает на мысль об использовании какой‑либо расширяемой структуры данных типа связного списка или расширяемого массива, такого как TList. Мы будем использовать (по крайней мере, пока) TList.
Следующий атрибут (возможность добавления элемента в очередь) легко реализовать в случае применения TList: достаточно вызвать метод Add структуры TList. Мы будем исходить из предположения, что добавляемыми в очередь по приоритету элементами будут каким‑то образом описанные объекты, свойством которых является их приоритет. В результате мы получаем Достаточно простой элемент, не отвлекающий наше внимание от функциональных возможностей очереди по приоритету.
Реализация третьего атрибута (возможности отыскания наивысшего приоритета и возвращения связанного с ним объекта с удалением его из обрабатываемой очереди по приоритету) несколько сложнее, но все же сравнительно проста. По существу мы выполняем итерационный просмотр элементов структуры TList, сравнивая приоритет каждого элемента с наибольшим обнаруженным приоритетом. Если приоритет данного элемента больше наибольшего обнаруженного до этого момента приоритета, мы помечаем индекс этого элемента как нового элемента с наибольшим приоритетом и переходим к следующему элементу. Этот поиск является простым последовательным поиском. После проверки всех элементов в структуре TList мы знаем, какой их них является наибольшим (его индекс был запомнен), и просто удаляем его из TList.
Пример кода простой очереди по приоритету приведен в листинге 9.1. В нем используется функция сравнения, которая передается очереди по приоритету при ее создании, и которая сравнивает приоритеты элементов. Таким образом, самой очереди по приоритету не нужно уметь сравнивать приоритеты (и, следовательно, знать, являются ли они числами, строками или чем‑либо еще): очередь просто вызывает функцию сравнения, передавая ей два элемента, приоритеты которых требуется сравнить. Обратите также внимание, что очереди не нужно знать, что представляют собой элементы. Она просто хранит их. Поэтому можно просто объявить использование указателей в очереди и при необходимости выполнять приведение типов.
Листинг 9.1. Простая очередь по приоритету, построенная на основе структуры TList type
TtdSimplePriQueuel = class private
FCompare : TtdCompareFunc;
FList : TList;
protected
function pqGetCount : integer;
public
constructor Create(aCompare : TtdCompareFunc);
destructor Destroy; override;
function Dequeue : pointer;
procedure Enqueue(aItem : pointer);
property Count : integer read pqGetCount;
end;
constructor TtdSimplePriQueuel.Create(aCompare : TdCompareFunc);
begin
inherited Create;
FCompare := aCompare;
FList := TList.Create;
end;
destructor TtdSimplePriQueuel.Destroy;
begin
FList.Free;
inherited Destroy;
end;
function TtdSimplePriQueuel.Dequeue : pointer;
var
Inx : integer;
PQCount : integer;
MaxInx : integer;
MaxItem : pointer;
begin
PQCount := Count;
if (PQCount = 0) then
Result := nil else
if (PQCount = 1) then begin
Result := FList.List^[0];
FList.Clear;
end
else begin
MaxItem := FList.List^ [0];
MaxInx := 0;
for Inx := 1 to pred(PQCount) do
if (FCompare (FList.List^ [Inx], MaxItem) > 0) then begin
MaxItem := FList.List^[Inx];
MaxInx := Inx;
end;
Result := MaxItem;
FList.List^[MaxInx] := FList.Last;
FList.Count := FList.Count ‑ 1;
end;
end;
procedure TtdSimplePriQueuel.Enqueue(aItem : pointer);
begin
FList.Add(aItem);
end;
function TtdSimplePriQueuel.pqGetCount : integer;
begin
Result := FList.Count;
end;
Из листинга 9.1 видно, что в действительности этот класс является достаточно простым, и даже добавление в него отсутствовавшей ранее проверки на наличие ошибок не делает его громоздким. Единственный фрагмент кода, который представляет интерес ‑ код удаления элемента: мы не вызываем метод Delete структуры данных TList (операция типа O(n)) а просто заменяем элемент, который нужно удалить, последним элементом и уменьшаем на единицу значение счетчика элементов (операция типа O(1)).
Исходный код класса TtdSimplePriQueuel можно найти на Web‑сайте издательства, в разделе материалов. После выгрузки материалов отыщите среди них файл TDPriQue.pas.
После того, как мы убедились в простоте разработки создания этой очереди по приоритету, рассмотрим ее эффективность. Во‑первых, добавление элемента в очередь по приоритету будет требовать постоянного времени. Иначе говоря, эта операция является операцией типа O(1). Независимо от того, содержит ли очередь ноль или тысячи элементов, добавление нового элемента будет занимать приблизительно одно и то же время: мы всего лишь дописываем его в конец списка.
Теперь рассмотрим противоположную операцию: удаление элемента. В этом случае для отыскания элемента с наивысшим приоритетом потребуется выполнить считывание всех элементов в структуре TList. Этот поиск является последовательным и, как было показано в главе 4, эта операция является операцией типа O(n). Требуемое для этого время пропорционально количеству элементов в очереди.
Таким образом, мы разработали и создали структуру данных, реализующую очередь по приоритету, в которой добавление элемента является операцией типа O(1), а удаление ‑ операцией типа O(n). При наличии небольшого количества элементов эта структура оказывается вполне приемлемой и достаточно эффективной.