Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 1997 / 0 | Длительность: 63:16:00
Лекция 9:

Очереди с приоритетами и пирамидальная сортировка

Аннотация: Рассмотрены структуры данных, состоящие из элементов с ключами (очереди с приоритетами)и методы их сортировки.

Во многих приложениях требуется обработка записей в порядке возрастания их ключей, но не обязательно в строгом порядке и не обязательно всех сразу. Часто записи накапливаются в некотором наборе, затем обрабатывается запись с максимальным ключом, после чего, возможно, продолжается накопление записей, потом обрабатывается запись с наибольшим текущим ключом и т.д. Соответствующая структура данных в такой среде поддерживает операции вставки нового элемента и удаления наибольшего элемента. Такая структура данных называется очередью с приоритетами (priority queue). Использование очередей с приоритетами похоже на использование обычных очередей (удаляется самый старый элемент) и стеков (удаляется самый новый элемент), однако эффективно реализовать их гораздо труднее. Очередь с приоритетами является наиболее важным примером АТД обобщенной очереди, который обсуждался в "Абстрактные типы данных" . Фактически очередь с приоритетами представляет собой обобщение стека и очереди, поскольку эти структуры данных можно реализовать через очереди с приоритетами, используя соответствующие правила назначения приоритетов (см. упражнения 9.3 и 9.4).

Определение 9.1. Очередь с приоритетами — это структура данных, состоящая из элементов с ключами, которая поддерживает две основные операции: вставить (insert) новый элемент и извлечь (remove) элемент с наибольшим ключом.

Вот несколько примеров применения очередей с приоритетами: системы моделирования, в которых ключи могут соответствовать моментам возникновения событий, для обработки их в хронологическом порядке; системы планирования заданий в вычислительных системах, где ключи могут соответствовать приоритетам, указывающим, какой из пользователей должен быть обслужен первым; а также численные расчеты, в которых ключами могут быть погрешности вычислений, в которых приоритеты показывают, что максимальная погрешность должна быть обработана первой.

Любую очередь с приоритетами можно использовать в качестве основы алгоритма сортировки, поместив в очередь все записи, а затем последовательно выбирая из нее записи с наибольшим ключом — получится обратно упорядоченная последовательность записей. Далее в этой книге мы увидим, как можно использовать очередь с приоритетами в качестве строительных блоков для более сложных алгоритмов. В части V мы разработаем алгоритм сжатия файлов, основанный на подпрограммах из данной главы, а в "Быстрая сортировка" увидим, как абстракция очередей с приоритетами облегчает понимание взаимоотношений между множеством фундаментальных алгоритмов поиска в графах. Это лишь несколько примеров той важной роли, которую играют очереди с приоритетами как базовые инструментальные средства при разработке алгоритмов.

На практике очереди с приоритетами обычно сложнее, чем это следует из только что сформулированного простого определения, поскольку для их использования в реальных ситуациях может потребоваться выполнение и некоторых других операций. Вообще-то одной из основных причин широкого практического применения многих реализаций очередей с приоритетами является их гибкость, позволяющая клиентским прикладным программам выполнять различные операции над множествами записей с ключами. Часто бывает нужно создавать и использовать структуры данных, содержащие записи с числовыми ключами (приоритетами), которые поддерживают некоторые из следующих операций:

  • Создать очередь с приоритетами из N заданных элементов,
  • Вставить новый элемент,
  • Извлечь наибольший элемент,
  • Изменить приоритет некоторого заданного элемента,
  • Извлечь некоторый заданный элемент,
  • Объединить две очереди с приоритетами в одну.

Если у записей могут быть повторяющиеся ключи, мы считаем, что " наибольший " значит " любая запись с максимальным значением ключа " . Как и в случае других структур данных, в этот набор еще нужно добавить стандартные операции создать, проверить наличие элементов и, возможно, операции уничтожить и копировать.

Эти операции кое в чем перекрываются, а в некоторых случаях удобно определить другие, аналогичные операции. Например, в каких-то клиентских программах может часто возникать необходимость найти наибольший элемент в очереди с приоритетами, но не обязательно удалить его. Или может понадобиться операция заменить наибольший элемент новым элементом. Такие операции можно реализовать, используя в качестве строительных блоков две базовые операции: операцию найти наибольший можно выполнить с помощью операций извлечь наибольший и затем вставить, а операцию заменить наибольший можно выполнить с помощью либо операций вставить и затем извлечь наибольший, либо операций извлечь наибольший и затем вставить. Однако более эффективный программный код получается при непосредственной реализации таких операций — если, конечно, они необходимы и точно определены. Точное определение не всегда однозначно, как это может показаться на первый взгляд. Например, два только что сформулированных варианта операции заменить наибольший существенно отличаются друг от друга: первый из них временно увеличивает размер очереди на один элемент, а второй — временно уменьшает. Подобным же образом операция изменить приоритет может быть реализована через операцию извлечь с последующей операцией вставить, а операция создать может быть реализована с помощью многократного применения операции вставить.

В некоторых ситуациях может оказаться чуть более удобным извлечение не наибольшего элемента, а наименьшего. Мы будем работать главным образом с очередями с приоритетами, которые настроены на доступ к наибольшим ключам. Если будет нужна другая настройка, мы будем называть ее (т.е. очередь с приоритетами, позволяющая извлечь наименьший элемент) очередью с приоритетами, ориентированной на минимальный элемент (minimum-oriented).

Очередь с приоритетами является прототипом абстрактного типа данных (АТД, см. "Абстрактные типы данных" ): это четко определенный набор операций над данными, который является удобной абстракцией, позволяющей отделить прикладные программы (клиенты) от различных реализаций, которые будут рассмотрены в настоящей главе. Интерфейс, заданный программой 9.1, определяет самые основные операции над очередями с приоритетами; более полный интерфейс будет рассмотрен в разделе 9.5. Строго говоря, различные подмножества разных операций, которые мы предпочтем включить в свой рабочий набор, приводят к различным абстрактным структурам данных, но для очередей с приоритетами наиболее характерны операции извлечь наибольший и вставить, поэтому основное внимание будет уделено именно им.

Различные реализации очередей с приоритетами обладают различной скоростью выполнения разных операций, а различные приложения требуют эффективного выполнения разных наборов операций. Вообще-то различия в производительности — это, в принципе, единственные различия, которые могут возникнуть при использовании понятия абстрактного типа данных. Эта ситуация приводит к различным компромиссам в отношении затрат. В данной главе будут рассмотрены различные пути достижения этих компромиссов, которые приблизят нас к идеалу — к возможности выполнить операцию извлечь наибольший за логарифмическое время, а все остальные операции — за постоянное время.

Сначала это положение будет проиллюстрировано в разделе 9.1 при обсуждении нескольких элементарных структур данных для реализации очередей с приоритетами. Далее, в разделах 9.2—9.4, наше внимание будет сосредоточено на рассмотрении классической структуры данных, получившей название пирамидальное дерево, которая обеспечивает эффективную реализацию всех операций, кроме операции объединить. Кроме того, в разделе 9.4 будет рассмотрен важный алгоритм сортировки, естественным образом вытекающий из этих реализаций.

Программа 9.1. Базовый АТД очереди с приоритетами

Данный интерфейс определяет операции для простейшего типа очереди с приоритетами: инициализировать, проверить наличие, добавить новый элемент, извлечь наибольший элемент. Элементарные реализации этих функций с использованием массивов и связных списков требуют линейного времени в худшем случае, но в данной главе будут даны реализации, для которых время выполнения всех операций гарантированно не больше величины, пропорциональной логарифму количества элементов в очереди. Как обычно, параметр конструктора задает максимальное ожидаемое количество элементов в очереди, хотя некоторые реализации могут его игнорировать.

template <class Item>
class PQ
  { private :
      // Код, зависящий от реализации 
    public:
      PQ(int);
    int empty() const;
    void insert(Item);
    Item getmax();
  };
      

В разделах 9.5 и 9.6 будут более подробно проанализированы некоторые проблемы, связанные с разработкой полных АТД очереди с приоритетами. И в завершение, в разделе 9.7 мы рассмотрим более сложную структуру данных, получившую название биномиальной очереди, которая позволяет реализовать все операции (в том числе и операции объединить) с логарифмическим временем выполнения в худшем случае.

При изучении всех этих структур данных мы будем рассматривать как основные компромиссы — между связным и последовательным распределением памяти (см. "Элементарные структуры данных" ) — так и задачи оформления пакетов, пригодных для использования прикладными программами. В частности, некоторые из продвинутых алгоритмов, описанных далее в этой книге, представляют собой клиентские программы, использующие очереди с приоритетами.

Упражнения

9.1. В последовательности

р r I о * R * * I * T * Y * * * Q U E * * * U * E

буква означает операцию вставить, а звездочка — операцию извлечь наибольший. Приведите последовательность значений, возвращаемых операциями извлечь наибольший.

9.2. Добавьте к условиям упражнения 9.1 знак плюс, означающий операцию объединить, и скобки для ограничения очереди с приоритетами, построенной операциями внутри этих скобок. Приведите содержимое очереди с приоритетами после выполнения последовательности операций

( ( ( P R I O ) + ( R * I T * Y * ) ) * * * ) + ( Q U E * * * U * E )

9.3. Объясните, как использовать АТД очереди с приоритетами для реализации АТД стека.

9.4. Объясните, как использовать АТД очереди с приоритетами для реализации АТД очереди.

Дмитрий Уколов
Дмитрий Уколов
Михаил Новопашин
Михаил Новопашин
Елена Лоповок
Елена Лоповок
Россия, г. Казань
Василий Крюк
Василий Крюк
Беларусь, Витебская область, Миорский район, д. Подъельцы, ул. Дворносельская, д. 12