web-dev-qa-db-fra.com

Implémenter une file d'attente efficace dans Python

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
10
random_coder_101

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 .

Ce qu'il ne faut pas faire

Évitez de réinventer la roue en faisant rouler la vôtre à la main:

  • Implémentation de la liste liée . Tout en réduisant ainsi la complexité temporelle la plus défavorable de vos méthodes 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.
  • implémentation de la liste Python . Comme je note ci-dessous , l'implémentation des méthodes 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 pour pop(0) et insert(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).

Que faire

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

Il est dangereux de partir seul

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 comme d[-1].

Utilisez simplement deque partout où une file d'attente simple ou double est requise. C'est tout.

15
Cecil Curry

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
4
Shaon shaonty

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()
1
Darshan N S

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
0
Yossarian42