Курс по структурам данных: https://stepik.org/a/134212
Мы продолжаем знакомство с очередями. На
этом занятии рассмотрим коллекцию deque модуля collections стандартной
библиотеки языка Python.
Класс deque, по сути,
реализует двусвязный список со всеми наборами операций: добавления/удаления
граничных элементов, вставки и удаления промежуточных, а также доступ к
произвольным элементам. Поэтому коллекцию deque в Python также можно
использовать, как полноценный двухсвязный список.
Подробное
описание всех методов класса deque можно найти на
странице официальной документации:
https://docs.python.org/3/library/collections.html#collections.deque
В частности, там
отмечается, что операции добавления/удаления граничных значений выполняется за O(1) операций, а
вставки/удаления промежуточных элементов за O(n) операций.
Доступ к произвольным элементам также требует O(n) операций. Все
это следует учитывать, при использовании методов класса deque в своих
приложениях.
Давайте теперь
познакомимся с основными методами очереди deque. Я выделил
следующие:
Метод
|
Описание
|
Объем
вычислений
|
append()
|
Добавление
нового элемента справа (в конец списка)
|
O(1)
|
appendleft()
|
Добавление
нового элемента слева (в начало списка)
|
O(1)
|
pop()
|
Удаление
элемента справа (с конца списка)
|
O(1)
|
popleft()
|
Удаление
элемента слева (с начала списка)
|
O(1)
|
extend()
|
Добавление
нескольких m элементов
справа (в конец списка)
|
O(m)
|
extendleft()
|
Добавление
нескольких m элементов
слева (в начало списка)
|
O(m)
|
insert()
|
Вставка
нового элемента в произвольную позицию
|
O(n)
|
remove()
|
Удаление
элемента списка с указанным значением (удаляется первый найденный элемент с
заданным значением)
|
O(n)
|
clear()
|
Удаление
всех элементов списка
|
O(1)
|
copy()
|
Создание
копии двусвязного списка
|
O(n)
|
Чтобы
воспользоваться этими методами, вначале нужно импортировать класс deque, например, так:
from collections import deque
и создать объект
двухсторонней очереди. Общий синтаксис следующий:
class
collections.deque([iterable[, maxlen]])
Здесь iterable – любой
итерируемый объект для начальной инициализации очереди deque определенными
значениями; maxlen – максимальная
длина очереди (максимальное число элементов). Если значение maxlen не указывается
или принимает значение None, то очередь не ограничена по числу
элементов.
В самом простом
варианте пустая очередь создается командой:
Или же можно
сразу указать набор требуемых начальных значений:
dq = deque([1, 2, 3, 4, 5])
а также
дополнительно указать максимальное число элементов:
dq = deque([1, 2, 3, 4, 5], maxlen=5)
тогда коллекция dq будет содержать
максимум 5 элементов.
Примеры использования методов класса deque
Выведем объект dq на экран и
убедимся, что он действительно содержит указанные начальные значения:
А что будет,
если в конец очереди (справа) добавить еще один элемент:
Смотрите, теперь
очередь содержит значения:
deque([2, 3, 4, 5, 6], maxlen=5)
То есть, при
указании максимального числа элементов и добавлении граничного значения, другое
крайнее значение удаляется. В этом легко убедиться, если выполнить добавление
справа:
и мы видим
результат:
deque([6, 1, 2, 3, 4], maxlen=5)
Но, давайте
уберем параметр maxlen и определим очередь с неограниченным
числом элементов:
dq = deque([1, 2, 3, 4, 5])
Тогда при
добавлении новых значений в начало или в конец очереди прежние данные теряться
не будут:
dq.append(6)
dq.appendleft(0)
Если же нам
нужно удалить граничный элемент справа или слева в двусвязном списке, то
следует воспользоваться методами:
right = dq.pop()
left = dq.popleft()
print(left, right)
print(dq)
Увидим результат
два значения 0 и 6, а также оставшиеся элементы в очереди:
deque([1, 2, 3,
4, 5])
Но, имейте в
виду, что методы pop() и popleft() генерируют ошибку (исключение)
IndexError, если элемент отсутствует, то есть, список пустой. Например:
dq = deque()
right = dq.pop()
left = dq.popleft()
приведет к
ошибке. Поэтому правильнее будет эти операторы поместить в блок try/except:
dq = deque()
try:
right = dq.pop()
left = dq.popleft()
print(left, right)
print(dq)
except IndexError as e:
print(e)
Следующие два
метода extend() и extendleft() добавляют
сразу несколько элементов в конец или начало очереди. Работают они подобно
методам append() и appendleft(), только в
качестве аргументов указывается коллекция с данными:
dq = deque([1, 2, 3, 4, 5])
dq.extend((6, 7, 8))
dq.extendleft([-3, -2, -1])
print(dq)
Увидим
результат:
deque([-1, -2,
-3, 1, 2, 3, 4, 5, 6, 7, 8])
Обратите
внимание, что в качестве итерируемого объекта мы можем указывать и кортеж и
список и любой другой, например, множество, словарь и т.п.
Следующий метод insert() позволяет
вставлять новый элемент в произвольную позицию, например, так:
Получим
результат:
deque([1, 100,
2, 3, 4, 5])
То есть, первым
аргументом указывается индекс вставляемого элемента, а вторым – его значение.
Также можно
указывать и отрицательные индексы:
получим:
deque([1, 2, 3,
4, 100, 5])
И
несуществующие:
dq.insert(77, 100)
dq.insert(-77, -100)
результат:
deque([-100, 1,
2, 3, 4, 5, 100])
Далее, метод remove() позволяет
удалять первый найденный по значению элемент в очереди, например:
dq = deque([1, 2, 3, 4, 3, 5])
dq.remove(3)
Получим
результат:
deque([1, 2, 4,
3, 5])
Если значение не
находится:
то генерируется
исключение ValueError.
Следующий метод clear() удаляет все
элементы из очереди, то есть, очищает ее:
dq = deque([1, 2, 3, 4, 3, 5])
dq.clear()
получим пустую
коллекцию:
deque([])
И последний
метод copy(), который мы
рассмотрим на этом занятии, создает копию очереди:
dq = deque([1, 2, 3, 4, 3, 5])
dq2 = dq.copy()
dq2.append(100)
print(dq)
print(dq2)
Увидим
результат:
deque([1,
2, 3, 4, 3, 5])
deque([1, 2, 3,
4, 3, 5, 100])
Реализация очередей типа FIFO и LIFO
Вот основные
методы двухсторонней очереди deque языка Python. Используя их,
легко можно построить очереди по принципу FIFO и LIFO. Например,
реализацию FIFO можно выполнить
следующим образом:
dq.appendleft(10) # добавление в начало очереди
value = dq.pop() # удаление с конца очереди
Или, наоборот:
dq.append(10) # добавление в конец очереди
value = dq.popleft() # удаление с начала очереди
Очередь типа LIFO можно сделать с
помощью методов:
dq.append(11) # добавление в конец
value = dq.pop() # удаление с конца
Или, наоборот:
dq.appendleft(11) # добавление в начало
value = dq.popleft() # удаление с начала
Альтернативные
подходы дают тот же самый эффект, поэтому на практике очереди типов FIFO и LIFO можно
реализовывать как вам больше нравится или лучше соответствует структуре
программы.
Курс по структурам данных: https://stepik.org/a/134212