La cola lineal es la versión más intuitiva: se reserva una lista de tamaño fijo, se define un frente y un final y se desplazan ambos índices a medida que las solicitudes entran y salen. Es ideal para colas pequeñas o cuando el volumen de operaciones es moderado y previsiblemente inferior a la capacidad establecida.
Su mayor debilidad es el despericio de espacio cuando se desencolan elementos al comienzo y las posiciones liberadas no se reutilizan. Esto provoca el conocido problema de "falsa saturación" si se alcanzó el límite una vez.
TAM_MAX = 64
class ColaLineal:
def __init__(self, capacidad=TAM_MAX):
self.capacidad = capacidad
self.datos = [None] * capacidad
self.frente = 0
self.final = 0
def esta_vacia(self):
return self.frente == self.final
def esta_llena(self):
return self.final == self.capacidad
def encolar(self, valor):
if self.esta_llena():
raise OverflowError("Cola llena")
self.datos[self.final] = valor
self.final += 1
def desencolar(self):
if self.esta_vacia():
raise IndexError("Cola vacia")
valor = self.datos[self.frente]
self.frente += 1
return valor
Este diseño obliga a reinicializar la cola cuando frente alcanza a final y ambos valen TAM_MAX. En sistemas interactivos se suele copiar los elementos remanentes al comienzo o recurrir a colas circulares.
En la cola circular se reutilizan las posiciones liberadas volviendo a la posición cero mediante el operador módulo. Solo se necesita un contador de elementos activos para diferenciar entre cola llena y vacía cuando frente == final.
TAM_CIRC = 64
class ColaCircular:
def __init__(self, capacidad=TAM_CIRC):
self.capacidad = capacidad
self.datos = [None] * capacidad
self.frente = 0
self.final = 0
self.cantidad = 0
def encolar(self, valor):
if self.cantidad == self.capacidad:
raise OverflowError("Cola llena")
self.datos[self.final] = valor
self.final = (self.final + 1) % self.capacidad
self.cantidad += 1
def desencolar(self):
if self.cantidad == 0:
raise IndexError("Cola vacia")
valor = self.datos[self.frente]
self.frente = (self.frente + 1) % self.capacidad
self.cantidad -= 1
return valor
Las colas circulares aparecen en drivers, buffers de audio o protocolos de red porque encajan con la naturaleza repetitiva de los flujos.
El deque permite insertar y eliminar por ambos extremos. Se lo usa cuando la lógica requiere alternar prioridades, simular estructuras híbridas o implementar algoritmos de ventanas deslizantes.
En Python se apoya en collections.deque, que optimiza inserciones y extracciones en los extremos con complejidad O(1) amortizada.
from collections import deque
def demo_deque():
fila = deque()
fila.append("A") # push back
fila.append("B")
fila.appendleft("VIP") # push front
print("Frente:", fila[0])
print("Sale:", fila.popleft()) # pop front
print("Sale por atras:", fila.pop()) # pop back
if __name__ == "__main__":
demo_deque()
Cada operación mantiene la complejidad O(1) amortizada, lo que vuelve al deque una opción atractiva frente a combinaciones ad hoc de dos pilas o dos colas.
La cola de prioridad modifica la regla FIFO permitiendo que el elemento con mayor o menor prioridad (según la definición del problema) sea atendido antes. En Python se implementa eficientemente con heapq, que usa un heap binario representado sobre una lista.
Las operaciones de inserción y extracción logran complejidad O(log n) al reorganizar el heap. Esta estructura es indispensable para planificadores, rutas de menor costo o simulaciones con eventos.
import heapq
class ColaPrioridad:
def __init__(self):
self._heap = []
def encolar(self, prioridad, valor):
heapq.heappush(self._heap, (prioridad, valor))
def desencolar(self):
if not self._heap:
raise IndexError("Cola vacia")
prioridad, valor = heapq.heappop(self._heap)
return prioridad, valor
def peek(self):
return self._heap[0] if self._heap else None
if __name__ == "__main__":
cola = ColaPrioridad()
cola.encolar(2, "soporte estandar")
cola.encolar(1, "soporte urgente")
cola.encolar(3, "consulta general")
while cola.peek():
prioridad, valor = cola.desencolar()
print(f"Atendiendo ({prioridad}): {valor}")
Una cola de prioridad debe exponer también una operación peek para consultar el próximo elemento a salir sin modificar la estructura, y una función de comparación cuando se necesitan prioridades compuestas.