https://docs.python.org/3/library/queue.html
Kolejka to ADT, który pozwala zapisywać dane i następnie pobierać je w takiej kolejności, w jakiej je zapisano (ang. first in, first out, FIFO; pierwszy wchodzi, pierwszy wychodzi). Interfejs kolejki ma minimum cztery funkcje:
Jeżeli kolejka ma ograniczenie na liczbę przechowywanych elementów, to stosuje się jeszcze funkcję is_full(). Sytuacje prowadzące do błędu: przepełnienie kolejki, pobieranie elementu z pustej kolejki.
#from Queue import Queue # Py2 from queue import Queue # Py3 Q = Queue() # Q.put(item) # item = Q.get() # Q.empty() # czy pusta
Inna implementacja kolejki może korzystać z collections.deque i operacji len, clear, append+popleft lub appendleft+pop.
Implementacja kolejki FIFO za pomocą list Pythona. Robimy otoczkę wokół list stosując podejście obiektowe. Python sam wykryje próbę pobierania elementu z pustej kolejki.
class Queue:
def __init__(self):
self.items = []
def __str__(self): # podglądanie kolejki
return str(self.items)
def is_empty(self):
return not self.items
def is_full(self): # nigdy nie jest pusta
return False
def put(self, data):
self.items.append(data)
def get(self):
return self.items.pop(0) # mało wydajne!
Po zapisaniu tego kodu do osobnego modułu queue.py możemy w dowolnym programie korzystać z kolejek.
import queue
aqueue = queue.Queue() # inicjalizacja kolejki
for item in ["a", 2, 3.5]:
aqueue.put(item)
# Pobieranie elementów z kolejki w kolejności: "a", 2, 3.5
while not aqueue.is_empty():
print(aqueue.get())
Implementacja kolejki FIFO za pomocą listy jednokierunkowej.
class Node:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
class Queue:
def __init__(self):
self.head = None
def is_empty(self):
return not self.head
def put(self, data): # linear-time O(n)
node = Node(data) # next jest domyślnie None
if self.is_empty():
self.head = node
else: # dajemy na koniec listy
last = self.head
while last.next:
last = last.next
last.next = node
def get(self): # constat-time O(1)
data = self.head.data
self.head = self.head.next
return data
Metoda put() nie jest optymalna, ponieważ jest klasy O(n). Wymaga przejścia przez wszystkie węzły, aby dotrzeć do ostatniego węzła. Rozwiązaniem tego problemu jest dodatkowy atrybut tail w klasie Queue, w którym zapisana będzie referencja do ostatniego elementu.
Implementacja kolejki FIFO za pomocą listy jednokierunkowej. Dodajemy wskaźnik do ogona, aby poprawić wydajność listy.
class Node:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
class Queue:
def __init__(self):
self.head = None
self.tail = None
def is_empty(self):
return not self.head
def put(self, data): # constat-time O(1)
node = Node(data) # next jest domyślnie None
if self.is_empty():
self.head = self.tail = node
else: # dajemy na koniec listy
self.tail.next = node
self.tail = node
def get(self): # constat-time O(1)
data = self.head.data
self.head = self.head.next
if self.is_empty(): # zabezpieczenie
self.tail = None
return data
# Wykorzystanie klasy SingleList do budowy kolejki.
# Tutaj insert_tail() i remove_head() używają 'node.data', a nie 'node'.
import slist
class Queue(slist.SingleList):
put = slist.SingleList.insert_tail
get = slist.SingleList.remove_head
Implementacja kolejki FIFO za pomocą listy dwukierunkowej.
import dlist
class Queue(dlist.DoubleList):
put = dlist.DoubleList.insert_head
get = dlist.DoubleList.remove_tail
import dlist
class Queue(dlist.DoubleList):
put = dlist.DoubleList.insert_tail
get = dlist.DoubleList.remove_head
Implementacja kolejki FIFO za pomocą tablicy cyklicznej. Przy inicjalizacji kolejki trzeba podać maksymalną liczbę przechowywanych w kolejce elementów (size). Wykorzystywana w implementacji tablica będzie o jeden większa, aby przy pełnej tablicy head i tail były różne.
class Queue:
def __init__(self, size=5):
self.n = size + 1 # faktyczny rozmiar tablicy
self.items = self.n * [None]
self.head = 0 # pierwszy do pobrania
self.tail = 0 # pierwsze wolne
def is_empty(self):
return self.head == self.tail
def is_full(self):
return (self.tail + 1) % self.n == self.head
def put(self, data):
self.items[self.tail] = data
self.tail = (self.tail + 1) % self.n
def get(self):
data = self.items[self.head]
self.items[self.head] = None # usuwam referencję
self.head = (self.head + 1) % self.n
return data
Przykładowe operacje dla tablicy pięcioelementowej (size = 4, n = 5). kolejka pusta (head == tail) +--+--+--+--+--+ head = 0 (pierwszy zajęty) | | | | | | tail = 0 (pierwszy wolny) +--+--+--+--+--+ put(11) ; put(22) ; put(33) ; put(44) +--+--+--+--+--+ head = 0 |11|22|33|44| | tail = 4 +--+--+--+--+--+ kolejka pełna ((tail + 1) % n == head) get() ; get() +--+--+--+--+--+ head = 2 | | |33|44| | tail = 4 +--+--+--+--+--+ put(55) ; put(66) +--+--+--+--+--+ head = 2 |66| |33|44|55| tail = 1 +--+--+--+--+--+ kolejka pełna