J'ai essayé d'implémenter une file d'attente en Python et j'ai rencontré un problème.
J'essaie d'utiliser des listes pour implémenter la structure de données de la file d'attente, mais je n'arrive pas à comprendre comment faire enqueue
et dequeue
O(1) opérations.
Chaque exemple que j'ai vu en ligne, semble simplement ajouter l'opération enqueue
et supprimer le premier élément de la liste pour l'opération dequeue
. Mais cela rendrait l'opération dequeue
O(n) (où n est la taille de la liste) correcte?
Y a-t-il quelque chose de basique que j'ai manqué? Ou devez-vous utiliser LinkedLists pour implémenter efficacement une file d'attente?
import unittest
class Queue():
def __init__(self):
self._queue = []
self.size = 0
self.maxSize = 10
def enqueue(self, item):
if self.size < self.maxSize:
self._queue.append(item)
def dequeue(self):
'''
Removes an item from the front of the list. Remove first element of
the array
'''
first = self._queue[0]
del self._queue[0]
return first
Comme ri Goren astucieusement noté ci-dessus , le Python stdlib a déjà implémenté une file d'attente efficace en votre nom chanceux: collections.deque
.
Évitez de réinventer la roue en faisant rouler la vôtre à la main:
dequeue()
et enqueue()
à O (1), le type collections.deque
Le fait déjà. Il est également thread-safe et probablement plus d'espace et de temps, compte tenu de son héritage basé sur C.enqueue()
en termes de liste Python augmente sa complexité temporelle la plus défavorable à O (n). Depuis la suppression du dernier élément d'un tableau basé sur C et donc Python est une opération à temps constant, implémentant la méthode dequeue()
en termes de liste Python conserve la même complexité temporelle dans le pire des cas de O (1). Mais qui s'en soucie? enqueue()
reste pitoyablement lent.Pour citer la documentation officielle deque
:
Bien que les objets
list
prennent en charge des opérations similaires, ils sont optimisés pour des opérations rapides de longueur fixe et entraînent O(n) coûts de déplacement de la mémoire pourpop(0)
etinsert(0, v)
opérations qui modifient à la fois la taille et la position de la représentation sous-jacente des données.
Plus important encore, deque
aussi fournit une prise en charge prête à l'emploi pour une longueur maximale via le paramètre maxlen
passé au moment de l'initialisation, évitant ainsi la nécessité de tentatives manuelles de limiter la taille de la file d'attente (ce qui rompt inévitablement la sécurité des threads en raison des conditions de concurrence implicites dans les conditions if).
Au lieu de cela, implémentez votre classe Queue
en termes de type collections.deque
Standard comme suit:
from collections import deque
class Queue():
'''
Thread-safe, memory-efficient, maximally-sized queue supporting queueing and
dequeueing in worst-case O(1) time.
'''
def __init__(self, max_size = 10):
'''
Initialize this queue to the empty queue.
Parameters
----------
max_size : int
Maximum number of items contained in this queue. Defaults to 10.
'''
self._queue = deque(maxlen=max_size)
def enqueue(self, item):
'''
Queues the passed item (i.e., pushes this item onto the tail of this
queue).
If this queue is already full, the item at the head of this queue
is silently removed from this queue *before* the passed item is
queued.
'''
self._queue.append(item)
def dequeue(self):
'''
Dequeues (i.e., removes) the item at the head of this queue *and*
returns this item.
Raises
----------
IndexError
If this queue is empty.
'''
return self._queue.pop()
La preuve est dans le pudding infernal:
>>> queue = Queue()
>>> queue.enqueue('Maiden in Black')
>>> queue.enqueue('Maneater')
>>> queue.enqueue('Maiden Astraea')
>>> queue.enqueue('Flamelurker')
>>> print(queue.dequeue())
Flamelurker
>>> print(queue.dequeue())
Maiden Astraea
>>> print(queue.dequeue())
Maneater
>>> print(queue.dequeue())
Maiden in Black
En fait, ne faites pas cela non plus.
Il vaut mieux utiliser simplement un objet deque
brut plutôt que d'essayer d'encapsuler manuellement cet objet dans un wrapper Queue
. La classe Queue
définie ci-dessus est donnée seulement comme une démonstration triviale de l'utilitaire à usage général de l'API deque
.
La classe deque
fournit beaucoup plus de fonctionnalités , notamment:
... itération, décapage,
len(d)
,reversed(d)
,copy.copy(d)
,copy.deepcopy(d)
, test d'appartenance avec l'opérateur in et références d'indices tels commed[-1]
.
Utilisez simplement deque
partout où une file d'attente simple ou double est requise. C'est tout.
Vous pouvez conserver les nœuds tête et queue au lieu d'une liste de files d'attente dans queue class
class Node(object):
def __init__(self, item = None):
self.item = item
self.next = None
self.previous = None
class Queue(object):
def __init__(self):
self.length = 0
self.head = None
self.tail = None
def enqueue(self, x):
newNode = Node(x)
if self.head == None:
self.head = self.tail = newNode
else:
self.tail.next = newNode
newNode.previous = self.tail
self.tail = newNode
self.length += 1
def dequeue (self):
item = self.head.item
self.head = self.head.next
self.length -= 1
if self.length == 0:
self.last = None
return item
Implémentation de la file d'attente à l'aide de la liste en Python, gestion de la mise en file d'attente et de la mise en file d'attente selon la structure de données de la file d'attente intégrée:
class queue:
def __init__(self, max_size, size=0, front=0, rear=0):
self.queue = [[] for i in range(5)] #creates a list [0,0,0,0,0]
self.max_size = max_size
self.size = size
self.front = front
self.rear = rear
def enqueue(self, data):
if not self.isFull():
self.queue[self.rear] = data
self.rear = int((self.rear + 1) % self.max_size)
self.size += 1
else:
print('Queue is full')
def dequeue(self):
if not self.isEmpty():
print(self.queue[self.front], 'is removed')
self.front = int((self.front + 1) % self.max_size)
self.size -= 1
else:
print('Queue is empty')
def isEmpty(self):
return self.size == 0
def isFull(self):
return self.size == self.max_size
def show(self):
print ('Queue contents are:')
for i in range(self.size):
print (self.queue[int((i+self.front)% self.max_size)])
# driver program
q = queue(5)
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.enqueue(5)
q.dequeue()
q.show()
Voici mon implémentation de Queue en utilisant un tableau, enqueue
et dequeue
sont les deux O(1) opérations. L'implémentation est basée sur CLRS.
class Queue:
def __init__(self, length):
"""a queue of at most n elements using an array of n+1 element size"""
self.length = length
self.queue = [None]*(length+1)
self.head = 0
self.tail = 0
def enqueue(self, x):
if self.is_full():
return 'Overflow'
self.queue[self.tail] = x
if self.tail == self.length:
self.tail = 0
else:
self.tail = self.tail + 1
def dequeue(self):
if self.is_empty():
return 'Underflow'
x = self.queue[self.head]
if self.head == self.length:
self.head = 0
else:
self.head = self.head + 1
return x
def is_empty(self):
if self.head == self.tail:
return True
return False
def is_full(self):
if self.head == self.tail+1 or (self.head == 0 and self.tail == self.length):
return True
return False