Queue

Очередь — структура данных, в которой элементы добавляются с одного конца и
удаляются с другого.

Главное правило очереди:

FIFO = First In, First Out

То есть:

первый добавленный элемент удаляется первым

У очереди есть два конца:

front -> начало, откуда элементы удаляются
back  -> конец, куда элементы добавляются

Пример:

enqueue(7)
enqueue(3)
enqueue(9)
enqueue(1)

Состояние очереди:

front              back
  7    3    9    1

Если выполнить:

dequeue()

то будет удалён 7, потому что он был добавлен первым.

Термины

queue        -> очередь
front        -> первый элемент
back         -> последний элемент
size         -> количество элементов в очереди
enqueue(x)   -> добавить x в конец очереди
dequeue()    -> удалить и вернуть первый элемент
peek()       -> посмотреть первый элемент без удаления
isEmpty()    -> проверить, пуста ли очередь

Очередь задаётся тремя вещами:

1. набор элементов
2. порядок добавления элементов
3. два конца: front для удаления и back для добавления

Структура очереди

Структура отвечает на вопрос:

Что внутри и как расположено?

У очереди есть:

элементы
начало
конец
порядок добавления
размер

Очередь можно представить как горизонтальную последовательность:

front                         back
  ↓                            ↓
[7] -> [3] -> [9] -> [1]

Новые элементы добавляются в конец. Удаляются из начала.

Свойства

Короткая карта свойств очереди как структуры:

front               -> точка удаления элементов
back                -> точка добавления элементов
FIFO                -> первым вошёл, первым вышел
упорядоченность     -> порядок элементов имеет значение
размер              -> количество элементов в очереди
пустота             -> очередь может не содержать элементов
ограниченный доступ -> доступен только первый элемент

Конкретная очередь содержит конечное число элементов:

size = n
пустая очередь = size == 0
непустая очередь = size > 0

Для непустой очереди существуют начало и конец:

front(queue)
back(queue)

Если в очереди один элемент, начало и конец указывают на один и тот же элемент:

queue = [7]
front = 7
back = 7

Важно отделять свойства очереди от характеристик конкретной очереди:

свойство:       у очереди есть начало
характеристика: первый элемент этой очереди = 7

свойство:       у очереди есть размер
характеристика: размер этой очереди = 4

FIFO

Очередь работает по правилу:

First In, First Out

Если элементы добавлены в таком порядке:

7, 3, 9, 1

то удаляться они будут в том же порядке:

7, 3, 9, 1

Ограниченный доступ

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

arr[i]      -> обычная операция массива
queue[i]    -> не базовая операция очереди

Очередь скрывает внутренние позиции и даёт работать с началом и концом:

enqueue
dequeue
peek

Пустота

Очередь может быть пустой:

queue = []
size = 0

Для пустой очереди нельзя корректно выполнить:

dequeue()
peek()

если реализация явно не задаёт специальное поведение для этих случаев.

Базовые операции

Создание

queue = []

Обычно:

O(1)

Enqueue

Добавить элемент в конец очереди:

enqueue(queue, x)

Пример:

queue = [7, 3, 9]
enqueue(1)
queue = [7, 3, 9, 1]

Обычно:

O(1)

Dequeue

Удалить и вернуть первый элемент:

dequeue(queue)

Пример:

queue = [7, 3, 9, 1]
dequeue() -> 7
queue = [3, 9, 1]

Условие:

size(queue) > 0

Обычно:

O(1)

Peek / Front

Посмотреть первый элемент без удаления:

peek(queue)
front(queue)

Пример:

queue = [7, 3, 9, 1]
peek() -> 7
queue = [7, 3, 9, 1]

Условие:

size(queue) > 0

Сложность:

O(1)

Is Empty

Проверить, пуста ли очередь:

isEmpty(queue)

Пример:

[]          -> true
[7, 3, 9]   -> false

Сложность:

O(1)

Size

Получить количество элементов:

size(queue)

Обычно:

O(1)

если размер хранится отдельно.

Проход по очереди

Если нужно обработать все элементы через операции очереди, её можно постепенно
разбирать:

while not isEmpty(queue):
    x = dequeue(queue)
    use x

Сложность:

O(n)

Важно: такой проход изменяет очередь, потому что dequeue() удаляет элементы.

Таблица сложностей

Операция Сложность
создание O(1)
enqueue(x) O(1)
dequeue() O(1)
peek() / front() O(1)
isEmpty() O(1)
size() O(1)
проход с удалением всех элементов O(n)
поиск произвольного значения O(n)

Реализация

Очередь — это абстрактная структура данных. Её можно реализовать разными
способами.

Через связный список

Нужно хранить ссылки на начало и конец:

front                         back
  ↓                            ↓
[7] -> [3] -> [9] -> [1]

Тогда:

enqueue(x) -> добавить новый узел после back
dequeue()  -> удалить узел front
peek()     -> прочитать front

Все основные операции работают за O(1), если есть ссылки на front и back.

Через кольцевой буфер

Очередь можно хранить в массиве фиксированной ёмкости с двумя указателями:

capacity = 8
front = 2
back = 6

index:  0  1  2  3  4  5  6  7
value:  _  _  7  3  9  1  _  _

При добавлении back сдвигается вперёд. При удалении front сдвигается вперёд.

Когда указатель доходит до конца массива, он переходит в начало:

next(i) = (i + 1) mod capacity

Если буфер заполнен, реализация может создать новый буфер большей ёмкости и
перенести элементы. Тогда отдельное расширение стоит O(n), но добавление
остаётся амортизированно быстрым.

Через динамический массив

Если добавлять в конец и удалять из начала обычного динамического массива:

enqueue(x) -> append(x)
dequeue()  -> remove first element

то удаление из начала обычно стоит:

O(n)

потому что все оставшиеся элементы нужно сдвинуть влево.

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

Очередь, стек и дек

Стек удаляет последний добавленный элемент:

stack -> LIFO

Очередь удаляет первый добавленный элемент:

queue -> FIFO

Дек обобщает очередь:

deque -> операции доступны с обоих концов

Обычную очередь можно получить из дека:

enqueue(x) -> pushBack(x)
dequeue()  -> popFront()

Python

В Python для обычной очереди часто используют:

collections.deque

Основные операции:

append(x)   -> enqueue, добавить в конец
popleft()   -> dequeue, удалить из начала
d[0]        -> peek, посмотреть первый элемент

Пример:

from collections import deque

q = deque()

q.append(7)
q.append(3)
q.append(9)

print(q[0])       # 7
print(q.popleft())  # 7
print(q)          # deque([3, 9])

Для потокобезопасной очереди в стандартной библиотеке есть:

queue.Queue

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

Типичные применения

BFS
очередь задач
обработка событий
планирование запросов
буферизация данных
симуляции
producer-consumer

Формальная схема

Queue = {
  elements: e1, e2, ..., en,
  front: e1,
  back: en,
  size: n,
  rule: FIFO
}

Операции очереди изменяют или используют одну из четырёх сущностей:

начало
конец
размер
порядок удаления

Примеры:

enqueue(x) -> меняет конец и увеличивает размер
dequeue()  -> возвращает начало и уменьшает размер
peek()     -> читает начало без изменения очереди
isEmpty()  -> проверяет размер

Минимальная формулировка:

очередь = структура с доступом к первому добавленному элементу
Прокрутить вверх