Стек, очередь, очередь по приоритету
Сайт: | Информатикс |
Курс: | «Олимпиадное программирование» - курс для начинающих |
Книга: | Стек, очередь, очередь по приоритету |
Напечатано:: | Гость |
Дата: | Среда, 3 Сентябрь 2025, 15:12 |
1. Куча и очередь с приоритетом Python
Очередь с приоритетом – это такая коллекция, которая поддерживает обязательно две следующие операции: вставка элемента с некоторым приоритетом (это может быть число или другой сравнимый объект) и извлечение элемента с наибольшим (или наименьшим приоритетом).
Очередь с приоритетом эффективно реализовать с помощью кучи. Двоичная куча – бинарное дерево, где значение в любой вершине не меньше (больше), чем значения её потомков, при этом заполнение уровней должно быть последовательным (без дырок и перескоков на следующий уровень, если текущий уровень не закончен). Работа с кучей осуществляется за время порядка O(log n). А построение кучи из массива – за O(n), где n – число элементов.
Кучу удобно хранить в массивах. Напомню, что списки (list) в Python – это и есть массивы переменной длины, а не связные списки (linked list), как в других языках. Поэтому доступ к элементу по индексу осуществляется за O(1). Пускай в корень дерева мы положим наименьший элемент, а корнем дерева будет нулевой элемент списка heap[0]. Его потомками будут элементы heap[1]
и heap[2]
. В общем виде потомками элемента heap[k]
будут heap[2*k+1]
и heap[2*k+2]
. Такая индексация позволяет хранить в массиве двоичное дерево компактно и удобно получать доступ к его элементам. По условиям кучи должны соблюдаться условия heap[k] <= heap[2*k+1]
и heap[k] <= heap[2*k+2]
.
В Python есть модуль heapq, который предоставляет процедурный интерфейс по работе с двоичными кучами, причем его функции принимают в качестве кучи именно обычные объекты типа list.
Создать кучу можно двумя способами:
- начать с пустого списка [] и заполнить его методом heappush
- взять существующий список и превратить его в кучу на месте (in-place) функцией heapify. heapify не возвращает новый список, а модифицирует переданный:
Основные функции:
▶ heappush(heap, item)
– добавляет в кучу элемент item.
▶ heappop(heap)
— возвращает наименьший элемент и удаляет его из кучи. Если не надо удалять, то просто heap[0]
.
▶ heappushpop(heap, item)
– добавляет в кучу item, а после возвращает наименьший элемент (работает немного быстрее, чем в две операции).
▶ heapreplace(heap, item)
– возвращает наименьший элемент, а потом уже добавляет новый item (работает немного быстрее, чем в две операции).
ℹ️ Помните! Отсортированный список является кучей – это легко проверить по условиям. Поэтому если ваш массив заранее отсортирован, то не надо вызывать heapq.heapify.
ℹ️ Но не любая куча – отсортированный список. Построение из массива – кучи, а из кучи – отсортированного массива – это сортировка кучей: мы наполняем последовательно кучу, а потом по порядку извлекаем наименьшие элементы, пока куча не истощится:
В примерах выше, мы пользовались числами, как элементами кучи, потому что числа легко сравнимы. В реальной жизни мы оперируем произвольными объектами с их приоритетами. Для перехода от кучи к очереди с приоритетами можно использовать следующий трюк: использовать как элементы кортежи вида (приоритет, объект) или (приоритет, идентификатор, объект), если объекты несравнимы. Приоритет – число (например: важность задачи или время запуска какого-либо процесса). Идентификатором может служить порядковый номер.
Куча возвращает минимальный элемент (heappop), а кортежи сравниваются поэлементно слева направо. Сначала идет -priotiry. Знак минус, чтобы сначала возвращался не минимальный приоритет, а максимальный. Если приоритеты равны, то будет возвращен элемент с наименьшим идентификатором ident, т.е. тот, что был добавлен раньше всех для данного значения приоритета.
2. Стеки, очереди, деки в STL
Язык программирования C++ содержит библиотеку STL (Standart template library, стандартная библиотека шаблонов). В библиотеку STL входят шаблоны для реализации стеков, деков, очередей для произвольных типов данных. Например, чтобы создать стек целых чисел необходимо использовать тип stack<int>
, для создания стека строк – stack<string>
, для создания стека объектов типа Person
– stack<Person>
.
Рассмотрим подробнее стеки, очереди, деки в STL. Описание шаблонов stack
, queue
, deque
содержится в одноименных заголовочных файлах, то есть для использования этих структур необходимо подключить один из трех заголовочных файлов:
#include<stack>
#include<queue>
#include<deque>
После этого можно создавать объекты на основе данных шаблонов:
stack<int> S; // Стек объектов типа int
queue<double> Q; // Очередь объектов типа double
deque<string> D; // Дек объектов типа string
После этого с объявленными объектами можно выполнять операции, например:
S.push(3); // Положить в стек S число 3
Q.size(); // Узнать размер очереди Q
D.pop_front(); // Удалить элемент из начала дека D
Рассмотрим более подробно список методов, присущих шаблонам stack<type>
, queue<type>
, deque<type>
(type
T – это тип данных, хранящихся в соответствующей структуре).
Обратите внимание, что методы удаления pop
, pop_front
и pop_back
в STL не возвращают значения. Кроме того, реализации стека, очереди, дека в STL не содержат проверок на возникновение ошибок, то есть за некорректную работу программы при вызове операций на пустом стеке отвечает сам пользователь.
Метод очистки clear
существует только у дека.
Методы шаблона stack<type>
- type top()
- Узнать значение верхнего элемента в стеке
- void push(type)
- Добавить элемент в конец стека
- void pop()
- Удалить верхний элемент из стека
- int size()
- Узнать количество элементов в стеке
Методы шаблона queue<type>
- type front()
- Узнать значение первого элемента в очереди
- type back()
- Узнать значение последнего элемента в очереди
- void push(T)
- Добавить элемент в конец очереди
- void pop()
- Удалить первый элемент из очереди
- int size()
- Узнать количество элементов в очереди
Методы шаблона deque<type>
- T front()
- Узнать значение первого элемента в деке
- T back()
- Узнать значение последнего элемента в деке
- void push_front(T)
- Добавить элемент в начало дека
- void push_back(T)
- Добавить элемент в конец дека
- void pop_front()
- Удалить первый элемент из дека
- void pop_back()
- Удалить последний элемент из дека
- int size()
- Узнать количество элементов в деке
- void clear()
- Удаляет все элементы из дека