Опубликован: 05.01.2015 | Уровень: для всех | Доступ: платный
Лекция 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. Объясните, как использовать АТД очереди с приоритетами для реализации АТД очереди.

Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?

Александр Ефимов
Александр Ефимов
Россия, Спб, СпбГтурп
Павел Сусликов
Павел Сусликов
Россия