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

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

Элементарные реализации

Базовые структуры данных, рассмотренные в "Элементарные структуры данных" , предоставляют множество различных возможностей для реализации очередей с приоритетами. Программа 9.2 является реализацией, основанной на использовании неупорядоченного массива. Операция извлечь наибольший реализуется следующей последовательностью действий: сначала, просмотром всего массива, находится наибольший элемент, затем на его место копируется последний элемент, и размер очереди уменьшается на единицу. На рис. 9.1 показано содержимое такого массива при выполнении последовательности операций. Эта базовая реализация соответствует реализациям из "Абстрактные типы данных" для стеков и очередей (см. программы 4.4 и 4.11) и пригодна для очередей небольших размеров. Основные различия между ними связаны с их производительностью. Для стеков и очередей можно было разработать реализации всех операций, которые выполняются за постоянное время. Что же касается очередей с приоритетами, то легко написать такие реализации, в которых за постоянное время выполняется одна из функций вставить или извлечь наибольший, однако найти реализацию с быстрым выполнением обеих операций гораздо труднее — это и будет темой данной главы.

Программа 9.2. Реализация очереди с приоритетами на базе массива

Эта реализация, которую можно сравнить с реализациями стеков и очередей на базе массивов, рассмотренными в "Абстрактные типы данных" (см. программы 4.4 и 4.11), содержит элементы в неупорядоченном массиве. Элементы добавляются в конец массива и удаляются из конца массива, как и в стеке.

  template <class Item>
  class PQ
    { private:
      Item *pq;
      Int N;
      public:
        PQ (int maxN)
        { pq = new Item[maxN]; N = 0; }
      int empty() const
        {return N == 0; }
      void insert (Item item)
        { pq[N++] = item; }
      Item getmax( )
        { int max = 0;
for (int j = 1; j < N; j++)
  if (pq[max] < pq[j]) max = j;
exch(pq[max], pq[N-1]);
return pq[—N];
        }
    };
        
 Пример очереди с приоритетами (реализация на базе неупорядоченного массива)

Рис. 9.1. Пример очереди с приоритетами (реализация на базе неупорядоченного массива)

Здесь показан результат выполнения последователь ности операций, представленных в левом столбце (сверху вниз), где буква означает вставку, а звездоч ка — извлечение наибольшего элемента. В каждой строке приведена операция, буква, извлеченная операцией извлечь наибольший, и содержимое массива после выполнения операции.

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

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

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

Как видно из данной таблицы, содержащей временные показатели различных методов для худшего случая (с точностью до постоянного множителя для больших N), показатели производительности реализаций АТД очереди с приоритетами колеблются в широких пределах. Элементарные методы (первые четыре строки) требуют для выполнения некоторых операций постоянного времени и линейного времени для остальных; более совершенные методы гарантируют для большинства или даже всех операций логарифмическое или постоянное время.

Таблица 9.1. Затраты на операции над очередями с приоритетами в худшем случае
вставить извлечь наибольший извлечь найти наибольший изменить приоритет объединить
Упорядоченный массив N 1 N 1 N N
Упорядоченный список N 1 1 1 N N
Неупорядоченный массив 1 N 1 N 1 N
Неупорядоченный список 1 N 1 N 1 1
Пирамидальное дерево lgN lgN lgN 1 lgN N
Биномиальная очередь lgN lgN lgN lgN lgN lgN
Теоретически наилучший 1 lgN lgN 1 1 1

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

Упражнения

9.5. В чем недостаток следующей идеи: для реализации операции найти наибольший отслеживать максимальное значение, включенное в очередь с приоритетами на текущий момент, а затем возвращать это значение как результат операции?

9.6. Приведите содержимое массива после выполнения последовательности операций, показанной на рис. 9.1.

9.7. Напишите реализацию базового интерфейса очереди с приоритетами, основанную на использовании упорядоченного массива.

9.8. Напишите реализацию базового интерфейса очереди с приоритетами, основанную на использовании неупорядоченного связного списка. Совет: см. программы 4.8 и 4.14.

9.9. Напишите реализацию базового интерфейса очереди с приоритетами, основанную на использовании упорядоченного связного списка. Совет: см. программу 3.11.

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

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

9.12. Напишите клиентскую программу-драйвер для измерения производительности, которая использует функцию insert для заполнения очереди с приоритетами, затем в течение 1 секунды выполняет операции getmax и insert — и так многократно, используя случайные последовательности ключей различной длины. Программа должна измерять время, затраченное на каждое выполнение, и вывести таблицу или график среднего количества операций getmax, которое удалось выполнить за секунду.

9.13. Воспользуйтесь клиентской программой из упражнения 9.12 для сравнения реализации с помощью неупорядоченного массива из программы 9.2 с реализацией на основе неупорядоченного списка из упражнения 9.8.

9.14. Воспользуйтесь клиентской программой из упражнения 9.12 для сравнения реализаций на основе упорядоченного массива и упорядоченного списка из упражнений 9.7 и 9.9.

9.15. Напишите клиентскую программу-драйвер для тестирования функций интерфейса очереди с приоритетами из программы 9.1 на трудных и вырожденных случаях, которые могут встретиться в практических приложениях. Примеры самых простых случаев: уже упорядоченные ключи, ключи в обратном порядке, все одинаковые ключи и последовательности ключей с лишь двумя различными значениями.

9.16.( За этим упражнением на самом деле стоят 24 упражнения.) Докажите правильность границ, приведенных в таблица 9.1 для четырех элементарных реализаций, используя реализации операций вставить и извлечь наибольший из программы 9.2 и упражнений 9.7—9.9, а также неформальное описание методов реализации других операций. Считайте, что для операций извлечь, изменить приоритет и объединить имеется возможность прямого доступа к указываемому объекту.

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

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

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

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

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

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