Un deque es una cola doblemente terminada que permite insertar y extraer elementos tanto por el frente como por el final. Mantiene el orden de llegada, pero admite patrones de acceso más flexibles que una cola FIFO estricta.
Es la base de planificadores, caches LRU y estructuras de datos avanzadas como los algoritmos de ventana deslizante.
La interfaz mínima se compone de cuatro operaciones de modificación y dos accesos de solo lectura:
appendleft / append: insertan en el frente o en el final.popleft / pop: retiran y retornan el valor asociado.front / back: leen sin extraer, devolviendo None si la estructura está vacía.En Python collections.deque ya ofrece estas operaciones con complejidad O(1) amortizada.
Python provee collections.deque, que internamente es un buffer de bloques doblemente enlazados optimizado para inserciones y extracciones en los extremos.
Si necesitas un control total sobre nodos y recorridos, también puedes implementar tu propio deque con una lista doblemente enlazada.
collections.dequedeque del módulo collections es la opción recomendada en Python para doble extremo. Usa bloques enlazados y ofrece complejidad O(1) amortizada.
from collections import deque
def demo_deque():
d = deque()
d.append(10) # push back
d.appendleft(5) # push front
d.append(20)
print("front:", d[0])
print("back:", d[-1])
print("pop front:", d.popleft())
print("pop back:", d.pop())
print("estado final:", list(d))
if __name__ == "__main__":
demo_deque()
Los pop simétricos aplican el mismo principio y pueden integrarse en ciclos que consumen mientras la estructura no esté vacía.
Si necesitas personalizar validaciones o almacenar metadatos en cada nodo, puedes implementar tu propio deque con una lista doblemente enlazada.
class Nodo:
def __init__(self, dato):
self.dato = dato
self.anterior = None
self.siguiente = None
class DequeLista:
def __init__(self):
self.frente = None
self.final = None
self.cantidad = 0
def push_front(self, valor):
nuevo = Nodo(valor)
nuevo.siguiente = self.frente
if self.frente:
self.frente.anterior = nuevo
else:
self.final = nuevo
self.frente = nuevo
self.cantidad += 1
def push_back(self, valor):
nuevo = Nodo(valor)
nuevo.anterior = self.final
if self.final:
self.final.siguiente = nuevo
else:
self.frente = nuevo
self.final = nuevo
self.cantidad += 1
def pop_front(self):
if not self.frente:
return None
nodo = self.frente
self.frente = nodo.siguiente
if self.frente:
self.frente.anterior = None
else:
self.final = None
self.cantidad -= 1
return nodo.dato
def pop_back(self):
if not self.final:
return None
nodo = self.final
self.final = nodo.anterior
if self.final:
self.final.siguiente = None
else:
self.frente = None
self.cantidad -= 1
return nodo.dato
Este estilo evita reasignar buffers y mantiene la complejidad constante aun cuando el deque crece o se vacía repetidamente.
Siempre documenta si el deque es seguro para hilos o si requiere protección externa (mutex, spinlocks) antes de exponerlo a un scheduler.
El siguiente programa implementa un deque mediante lista doble y expone todas las operaciones, además de una función imprimir para depurar el estado.
class Nodo:
def __init__(self, dato):
self.dato = dato
self.anterior = None
self.siguiente = None
class Deque:
def __init__(self):
self.frente = None
self.final = None
self.cantidad = 0
def push_front(self, valor):
nuevo = Nodo(valor)
nuevo.siguiente = self.frente
if self.frente:
self.frente.anterior = nuevo
else:
self.final = nuevo
self.frente = nuevo
self.cantidad += 1
def push_back(self, valor):
nuevo = Nodo(valor)
nuevo.anterior = self.final
if self.final:
self.final.siguiente = nuevo
else:
self.frente = nuevo
self.final = nuevo
self.cantidad += 1
def pop_front(self):
if not self.frente:
return None
nodo = self.frente
self.frente = nodo.siguiente
if self.frente:
self.frente.anterior = None
else:
self.final = None
self.cantidad -= 1
return nodo.dato
def pop_back(self):
if not self.final:
return None
nodo = self.final
self.final = nodo.anterior
if self.final:
self.final.siguiente = None
else:
self.frente = None
self.cantidad -= 1
return nodo.dato
def imprimir(self):
elementos = []
actual = self.frente
while actual:
elementos.append(str(actual.dato))
actual = actual.siguiente
print(f"[deque] cantidad={self.cantidad} -> {' '.join(elementos)}")
if __name__ == "__main__":
deque = Deque()
deque.push_back(10)
deque.push_back(20)
deque.push_front(5)
deque.imprimir()
print("pop_front =", deque.pop_front())
deque.imprimir()
deque.push_front(1)
deque.push_back(99)
deque.imprimir()
print("pop_back =", deque.pop_back())
deque.imprimir()
Ejecuta el programa y observa cómo las operaciones afectan al contador y a los nodos interiores para validar que no quedan referencias colgantes.