Descubre las `deque`, una estructura de datos optimizada para añadir y quitar elementos rápidamente por ambos extremos.
En Python, además de las listas estándar [], existe una estructura de datos especializada muy útil llamada deque (pronunciada como "deck", la abreviatura de "double-ended queue" o "cola de doble extremo"). Las deques están disponibles en el módulo incorporado collections.
Una deque es una secuencia similar a una lista, pero con una gran ventaja: está altamente optimizada para realizar operaciones de añadir (append) y quitar (pop) elementos desde ambos extremos (izquierda y derecha) de manera muy rápida y eficiente.
deque sobre las Listas Estándar:lista.insert(0, valor) (añadir al principio) o lista.pop(0) (quitar del principio) son lentas (complejidad $O(N)$, donde N es el tamaño de la lista, porque todos los demás elementos deben desplazarse), en una deque, las operaciones equivalentes (appendleft(), popleft()) son muy rápidas (complejidad $O(1)$, tiempo constante).append() y pop(), y colas (FIFO - First In, First Out) usando append() y popleft().
Para usar una deque, primero debes importarla desde el módulo collections.
from collections import deque
# Crear una deque vacía
cola_de_tareas = deque()
# Crear una deque a partir de una lista existente
datos_sensor_iniciales = [10.1, 10.3, 10.2]
buffer_sensor = deque(datos_sensor_iniciales)
print(f"Deque inicial: {buffer_sensor}")
# Crear una deque con una longitud máxima (maxlen)
# Útil para historiales o buffers circulares
ultimas_lecturas = deque(maxlen=5)
print(f"Deque con longitud máxima (vacía): {ultimas_lecturas}")
💡 ¿Cuándo elegir deque? Si tu aplicación requiere añadir o quitar elementos frecuentemente del inicio de una secuencia, o si necesitas una cola o pila eficiente, ¡deque es tu mejor opción! Para acceso aleatorio por índice en el medio de la secuencia, las listas normales siguen siendo adecuadas.
Las deques comparten muchos métodos con las listas, pero tienen algunos específicos para operaciones eficientes en ambos extremos.
deque:| Método | Descripción Breve | Complejidad |
|---|---|---|
append(x) |
Añade x al extremo derecho (final) de la deque. |
$O(1)$ |
appendleft(x) |
Añade x al extremo izquierdo (inicio) de la deque. |
$O(1)$ |
pop() |
Elimina y devuelve el elemento del extremo derecho. Lanza IndexError si está vacía. |
$O(1)$ |
popleft() |
Elimina y devuelve el elemento del extremo izquierdo. Lanza IndexError si está vacía. |
$O(1)$ |
extend(iterable) |
Añade los elementos de iterable al extremo derecho. |
$O(k)$ (k=longitud del iterable) |
extendleft(iterable) |
Añade los elementos de iterable al extremo izquierdo. Los elementos se añaden en orden inverso al del iterable. |
$O(k)$ |
remove(value) |
Elimina la primera aparición de value. Lanza ValueError si no está. |
$O(N)$ |
clear() |
Elimina todos los elementos de la deque. | $O(N)$ |
rotate(n=1) |
Rota la deque n pasos a la derecha. Si n es negativo, rota a la izquierda. |
$O(N)$ |
reverse() |
Invierte los elementos de la deque in-place. | $O(N)$ |
count(x) |
Cuenta las apariciones de x. |
$O(N)$ |
index(x[, start[, stop]]) |
Devuelve el índice de la primera aparición de x. Lanza ValueError si no está. |
$O(N)$ |
maxlen (atributo) |
Tamaño máximo de la deque (si se especificó al crearla), o None. |
$O(1)$ |
Exploremos algunos de estos métodos con ejemplos:
append(x) y appendleft(x)Añadir elementos a ambos extremos.
from collections import deque
# Simulación de llegada de tareas a un procesador
tareas_pendientes = deque(["TareaC"])
tareas_pendientes.append("TareaD") # Añade al final (derecha)
tareas_pendientes.appendleft("TareaB") # Añade al inicio (izquierda)
tareas_pendientes.appendleft("TareaA")
print(f"Cola de tareas: {tareas_pendientes}")
# Salida: deque(['TareaA', 'TareaB', 'TareaC', 'TareaD'])
pop() y popleft()Eliminar y obtener elementos de ambos extremos.
# Procesando la cola de tareas (FIFO - First In, First Out)
primera_tarea = tareas_pendientes.popleft()
print(f"Procesando: {primera_tarea}") # Sale 'TareaA'
print(f"Tareas restantes: {tareas_pendientes}")
# Si fuera una pila (LIFO - Last In, First Out)
# ultima_tarea_anadida = tareas_pendientes.pop()
# print(f"Procesando (LIFO): {ultima_tarea_anadida}")
rotate(n)Rota los elementos. n > 0 rota a la derecha, n < 0 rota a la izquierda.
# Física: Simular un cifrado César o un carrusel
secuencia_original = deque(['A', 'B', 'C', 'D', 'E'])
secuencia_original.rotate(2) # Rota 2 posiciones a la derecha
print(f"Rotada +2: {secuencia_original}")
# Salida: deque(['D', 'E', 'A', 'B', 'C'])
secuencia_original.rotate(-1) # Rota 1 posición a la izquierda
print(f"Rotada -1: {secuencia_original}")
# Salida: deque(['E', 'A', 'B', 'C', 'D'])
maxlen (Atributo y Comportamiento)Mantiene un historial de tamaño fijo. Al añadir a una deque llena, los elementos del extremo opuesto se descartan.
# Biología: Últimas N mediciones de pH en un cultivo celular
ultimos_ph = deque(maxlen=3)
ultimos_ph.append(7.4)
ultimos_ph.append(7.3)
ultimos_ph.append(7.5)
print(f"Últimos pH: {ultimos_ph}") # deque([7.4, 7.3, 7.5], maxlen=3)
ultimos_ph.append(7.2) # Añade 7.2, y 7.4 (el más antiguo) se va
print(f"Después de añadir 7.2: {ultimos_ph}")
# Salida: deque([7.3, 7.5, 7.2], maxlen=3)
La eficiencia de las deques en operaciones de extremo las hace ideales para varios escenarios:
Una pila sigue el principio "Último en Entrar, Primero en Salir". Usa append() para apilar (push) y pop() para desapilar.
# Matemáticas: Evaluar expresiones con paréntesis
pila_operaciones = deque()
pila_operaciones.append("Operación1") # Push
pila_operaciones.append("Operación2") # Push
print(f"Pila: {pila_operaciones}")
elemento_sacado = pila_operaciones.pop() # Pop "Operación2"
print(f"Sacado: {elemento_sacado}, Pila ahora: {pila_operaciones}")
Una cola sigue el principio "Primero en Entrar, Primero en Salir". Usa append() para encolar y popleft() para desencolar.
# Simulación de cola de impresión (Informática)
cola_impresion = deque()
cola_impresion.append("DocumentoA.pdf")
cola_impresion.append("ImagenB.jpg")
print(f"Cola: {cola_impresion}")
trabajo_actual = cola_impresion.popleft() # Sale "DocumentoA.pdf"
print(f"Imprimiendo: {trabajo_actual}, Cola ahora: {cola_impresion}")
Mantener un historial limitado de acciones recientes (usando maxlen).
# Editor de texto simple
historial_acciones = deque(maxlen=5)
def realizar_accion(accion):
historial_acciones.append(accion)
print(f"Acción: {accion}, Historial: {list(historial_acciones)}")
realizar_accion("Escribir 'Hola'")
realizar_accion("Cambiar fuente")
realizar_accion("Guardar")
realizar_accion("Insertar imagen")
realizar_accion("Añadir tabla")
realizar_accion("Borrar párrafo") # "Escribir 'Hola'" se descarta
En teoría de grafos (Matemáticas/Computación), las deques son perfectas para implementar la cola en BFS.
# Ejemplo conceptual de BFS
# grafo = { 'A': ['B', 'C'], 'B': ['D'], ... }
# por_visitar = deque(['NodoInicial'])
# while por_visitar:
# nodo_actual = por_visitar.popleft()
# # procesar nodo_actual
# # añadir vecinos no visitados a por_visitar.append()
print("BFS usa deque para la cola de nodos por visitar.")
Analizar subconjuntos de datos de tamaño fijo a medida que llegan nuevos datos (Física/Estadística).
# Simulación de datos de un sensor de temperatura
datos_ventana = deque(maxlen=4)
nuevas_mediciones = [20.1, 20.3, 20.5, 20.4, 20.6, 20.7]
for medicion in nuevas_mediciones:
datos_ventana.append(medicion)
if len(datos_ventana) == datos_ventana.maxlen:
promedio_ventana = sum(datos_ventana) / len(datos_ventana)
print(f"Ventana: {list(datos_ventana)}, Promedio: {promedio_ventana:.2f}°C")
Has aprendido sobre las deques del módulo collections, una alternativa especializada y eficiente a las listas de Python cuando necesitas realizar operaciones frecuentes en ambos extremos de una secuencia.
Recuerda sus puntos fuertes:
appendleft() y popleft() con rendimiento $O(1)$.maxlen) para historiales o buffers.rotate() para manipulación de secuencias.
Si bien las listas son la estructura de secuencia de propósito general en Python, conocer y saber cuándo usar una deque puede optimizar significativamente tus algoritmos y estructuras de datos.
🚀 Desafío de Simulación:
Imagina un sistema de procesamiento de datos de un telescopio (Astronomía). Los datos llegan en paquetes. Usa una deque con maxlen para almacenar los últimos 5 paquetes de datos recibidos. Cada vez que llega un nuevo paquete, añádelo. Si la deque está llena, el más antiguo se descarta. Imprime el estado de la deque después de añadir cada nuevo paquete para ver cómo funciona el buffer. ¡Pruébalo en Google Colab!