Что такое очередь с приоритетами
Очередь с приоритетом
В этой статье вы познакомитесь с очередями с приоритетом и узнаете, как их реализовать в Python, Java, C и C++.
Очереди с приоритетом — разновидность очередей, в которой у каждого элемента есть свой приоритет. Обслуживаются они в соответствии со своими приоритетом. Если у элементов одинаковый приоритет, то обслуживаются они по их порядку в очереди.
Значение элемента, как правило, и определяет его приоритет.
То есть, у элемента с самым большим значением самый высокий приоритет. Правда, это не всегда так. Самый высокий приоритет может быть у элемента и с самым малым значением. В остальных случаях мы можем задавать приоритеты элементам по своему усмотрению.
Разница между очередью с приоритетом и обычной очередью
Обычная очередь подчиняется принципу FIFO «первый вошел — первый вышел». В очередях с приоритетом элементы удаляются в соответствии с их приоритетом. То есть, элемент с самым высоким приоритетом удаляется из очереди в первую очередь.
Реализация очереди с приоритетом
Очереди с приоритетом можно реализовать с помощью следующих структур данных: массив, связный список, куча и двоичное дерево поиска. Среди всех этих структур выделяется куча — это самый эффективный способ реализации очереди с приоритетом.мы
Именно поэтому в этом руководстве мы будем использовать кучи. Конкретно — max-кучи.
Ниже представлена сравнительная характеристика различных реализаций очередей с приоритетом.
Очередь (queue) в C++: легко и просто!
Всем привет! Для решения задач многие программисты используют структуру данных — очередь. Вы наверно когда-то слышали о ней, а возможно и применяли в своей программе.
Мы попытаемся ответить на несколько вопросов: что такое очередь, принцип работы очереди и какая у нее есть разновидность. Поехали!
Что такое очередь
В очереди, если вы добавите элемент, который вошел самый первый, то он выйдет тоже самым первым. Получается, если вы добавите 4 элемента, то первый добавленный элемент выйдет первым.
Чтобы понять принцип работы очереди вы можете представить себе магазинную очередь. И вы стоите посреди нее, чтобы вы оказались напротив кассы, сначала понадобится всех впереди стоящих людей обслужить. А вот для последнего человека в очереди нужно, чтобы кассир обслужил всех людей кроме него самого.
На рисунке слева находятся 7 чисел: 2, 4, 7, 1, 4, 9, 10. Если нам понадобится их извлечь, то извлекать мы будем в таком же порядке как они находятся на рисунке!
Так например чтобы извлечь число 4 нам понадобится сначала обслужить число 2, а потом уже и само число 4.
Хотя в стеке присутствует функция peek() (она позволяет обратится к элементу по индексу, подробнее вот здесь), в шаблоне очереди невозможно обратится к определенному элементу.
Но если вам нужно иметь доступ ко всем элементам очереди, то можете реализовать очередь через массив. Чуть ниже мы покажем как это сделать.
Как создать очередь в С++
Дальше для объявления очереди нужно воспользоваться конструкцией ниже.
Алгоритм. Очередь с приоритетом
В этой статье я покажу очередь с приоритетом с использованием linked list. Алгоритм простой и позволяет получить приоритетные сообщения раньше, чем остальные сообщения.
Также создаем глобальный указатель на начало очереди, прошу заметить, это всего лишь пример, разумеется в C++ можно создать это в классе, но так как в C будет только одна очередь, то она становиться глобальной как само собой разумеющееся. И также создаем pri указатель на указатель. Он будет хранить хвост каждого приоритета, чтобы можно было в linked-list сразу в нужное место всегда добавлять новые данные.
теперь создаем функцию и начальные значения. Здесь мы также ищем близжайший приоритет. например если у нас первым был приоритет 20, потом 14, а теперь 10. то мы линейно доходим до 14 и становимся в его хвост и добавляемся уже рядом с ним.
Теперь можно в бесконечном цикле идти по списку и добавлять в нужную позицию наше число. Начнем с того, если указатель n равен нулю.
Здесь в указываем чтобы root указывал на него как на начало. Отсюда начнется следующий отсчет.
Далее мы смотрим, если добавляемый приоритет выше, чем наше сообщение в очереди, то перед ним установим новый приоритет.
Также можно заметить, что в этом случае root опять меняет свою позицию, указав себя как начало в списке.
Далее смотрим если добавляемый приоритет ниже чем текущий приоритет.
Здесь всё должно быть понятно, что мы добавляем наше число куда нужно.
И осталось последнее.
Здесь мы смотрим, если он равен нашему приоритету, то дадим сначала выйти из очереди первому добавленному с таким приоритетом, а этот добавим за ним.
Теперь создадим функцию по выдачи из очереди нашего числа.
Всё, здесь мы выдаем номер и смещаем наш root. Так как это без много поточности, то здесь я не использовал мьютексы, но по хорошему, нужно поставить мьютекс на смену root и всё.
Теперь создадим остальное.
Чтобы было удобней проверить код, то выкладываю полный код.
Класс priority_queue
Класс-шаблон адаптера контейнера, который предоставляет ограничение функциональности, ограничивая доступ к верхнему элементу некоторого базового типа контейнера, который всегда является самым большим или имеет высший приоритет. Новые элементы можно добавлять в priority_queue, а верхний элемент priority_queue можно просмотреть или удалить.
Синтаксис
Параметры
Тип
Тип данных элемента для сохранения в priority_queue.
Контейнер
Тип базового контейнера, используемый для реализации priority_queue.
Комментарии
Элементы класса, Type оговоренные в первом параметре шаблона объекта очереди, являются синонимами Type и должны соответствовать типу элемента в базовом классе контейнера, Container указанном вторым параметром-шаблоном. Объект Type должен быть назначаемым, чтобы можно было скопировать объекты этого типа и присвоить значения переменным этого типа.
Добавление элементов в priority_queue и удаление элементов из него имеют логарифмическую сложность. Доступ к элементам в priority_queue имеет постоянную сложность.
В стандартной библиотеке C++ определено три типа адаптеров контейнера: стек (stack), очередь (queue) и очередь с приоритетом (priority_queue). Каждый ограничивает функциональность некоторого базового класса контейнеров для обеспечения точно управляемого интерфейса к стандартной структуре данных.
Класс Stack поддерживает структуру данных «последним пов основе» (ЛИФО). Хороший аналог такого подхода — стопка тарелок. Элементы (тарелки) можно вставлять, проверять или удалять только из верхней части стека, которая является последним элементом в конце базового контейнера. Ограничение на доступ только к верхнему элементу является причиной использования класса стека.
Класс Queue поддерживает структуру данных «первым поступил — первым обслужен» (FIFO). Хороший аналог такого подхода — очередь из людей к банковскому служащему. Элементы (люди) можно добавлять в конец очереди и удалять из начала очереди. Проверять можно как начало, так и конец очереди. Ограничение на доступ только к переднему и заднему элементам в таком подходе является причиной использования класса очереди.
Класс priority_queue упорядочивает элементы, чтобы наибольший элемент всегда находился сверху. Он поддерживает вставку элемента, а также проверку и удаление верхнего элемента. Хороший аналог такого подхода — очередь из людей, упорядоченная по возрасту, росту или любому другому критерию.
Конструкторы
Определения типов
Функции элементов
Требования
Заголовок: очереди>
Пространство имен: std
priority_queue::container_type
Тип, предоставляющий базовый контейнер для изменения.
Комментарии
Пример
priority_queue::empty
Проверяет, что priority_queue пуста.
Возвращаемое значение
true значение, если priority_queue пустой; false если priority_queue не пуста.
Пример
priority_queue::pop
Удаляет самый большой элемент в priority_queue с верхней позиции.
Комментарии
Для применения функции-члена класс priority_queue не должен быть пустым. Вверху priority_queue всегда находится самый большой элемент в контейнере.
Пример
priority_queue::priority_queue
Создает priority_queue, которая пуста или является копией диапазона объекта базового контейнера или другой priority_queue.
Параметры
_comp
Функция сравнения типа constTraits для упорядочивания элементов в priority_queue, по умолчанию используется функция сравнения базового контейнера.
_Cont
Базовый контейнер, из которого будет копироваться создаваемая priority_queue.
Правильно
Priority_queue, для которой создаваемый набор станет копией.
first
Положение первого элемента в диапазоне копируемых элементов.
last
Положение первого элемента после диапазона копируемых элементов.
Комментарии
Каждый из первых трех конструкторов задает пустую начальную priority_queue, второй также задает тип функции сравнения ( comp ), используемой при установлении порядка элементов, а третий явно указывает container_type ( _Cont ) для использования. Ключевое слово explicit подавляет определенные виды автоматического преобразования типов.
Четвертый конструктор задает копию priority_queue right.
Пример
priority_queue::push
Добавляет элемент в очередь приоритетов на основе приоритета элемента оператора size_type базового контейнера, измененного priority_queue.
Пример
См. пример объявления и использования в разделе size.
priority_queue::top
Возвращает константную ссылку на наибольший элемент в верхней части priority_queue.
Возвращаемое значение
Ссылка на самый крупный элемент, определяемый Traits функцией, объект priority_queue.
Комментарии
Для применения функции-члена класс priority_queue не должен быть пустым.
Пример
priority_queue::value_type
Тип, представляющий тип объекта, который хранится в виде элемента в priority_queue.
Комментарии
Тип является синонимом value_type базового контейнера, измененного priority_queue.
Очередь с приоритетом
Очередь с приоритетом (англ. priority queue ) — абстрактный тип данных в программировании, поддерживающий три операции:
Говоря другим языком, очередь с приоритетом позволяет хранить пары (ключ, значение) и поддерживает операции добавления пары, поиска пары с минимальным ключом и извлечения пары с минимальным ключом:
Очередь с приоритетом может хранить несколько пар с одинаковыми ключами.
В разных реализациях очереди с приоритетом семантика и названия операций могут отличаться.
Есть ряд реализаций в которых все три операции выполняются в худшем случае за время, ограниченное (см. «O» большое и «o» малое), где
— количество хранимых пар. Реализация «Фибоначчиева куча» интересна тем, что в среднем (амортизационно) эти три операции выполняет за время
и, кроме того, позволяет быстро (тоже за время
) выполнять дополнительную операцию слияния двух куч.
Примеры
Расширения очереди с приоритетом
Различные реализации очереди с приоритетом нередко расширяют её интерфейс следующими операциями: