Programación · Estructuras de datos
Pila y cola explicadas: LIFO vs FIFO con ejemplos en Python
Respuesta corta
Una pila es una colección en la que el último elemento añadido es el primero en salir (LIFO, last in first out), como una pila de platos. Una cola es una colección en la que el primer elemento añadido es el primero en salir (FIFO, first in first out), como la fila de una caja. Ambas permiten añadir y quitar en tiempo O(1). En Python, usa una list como pila y collections.deque como cola.
Qué problema resuelven las pilas y las colas
Muchos programas necesitan recordar elementos y procesarlos después en un orden fijo. Una pila te da primero el elemento más reciente, lo que encaja con el trabajo anidado: llamadas a funciones, historial de deshacer, emparejar paréntesis y búsqueda en profundidad. Una cola te da primero el elemento más antiguo, lo que encaja con una espera justa: trabajos de impresión, planificadores de tareas, colas de mensajes y búsqueda en anchura.
Ambas son tipos de datos abstractos: se definen por sus operaciones, no por cómo se almacenan. Puedes construir cualquiera de las dos sobre un arreglo o una lista enlazada.
Operaciones y nombres
| Operación | Pila (LIFO) | Cola (FIFO) |
|---|---|---|
| Añadir un elemento | push: poner encima | enqueue: añadir al final |
| Quitar un elemento | pop: sacar de arriba | dequeue: sacar del frente |
| Mirar sin quitar | peek / top | peek / front |
| Tipo en Python | list con append y pop | collections.deque con append y popleft |
| Orden de salida para la entrada 1, 2, 3 | 3, 2, 1 | 1, 2, 3 |
Ejemplo resuelto: push y pop paso a paso
Mete 1, 2 y 3 en ambas estructuras y luego quita dos elementos.
| Paso | Contenido de la pila (cima a la derecha) | Contenido de la cola (frente a la izquierda) |
|---|---|---|
| añadir 1 | [1] | [1] |
| añadir 2 | [1, 2] | [1, 2] |
| añadir 3 | [1, 2, 3] | [1, 2, 3] |
| quitar | devuelve 3, queda [1, 2] | devuelve 1, queda [2, 3] |
| quitar | devuelve 2, queda [1] | devuelve 2, queda [3] |
Implementación en Python
Una list de Python ya es una buena pila: append y pop() operan sobre el final de la lista en O(1) amortizado. Para una cola, no uses list.pop(0), porque desplaza todos los elementos restantes y cuesta O(n). Usa collections.deque, que es O(1) en ambos extremos.
from collections import deque
stack = []
stack.append(1)
stack.append(2)
stack.append(3)
print(stack.pop(), stack[-1]) # 3 2
queue = deque()
queue.append("a")
queue.append("b")
queue.append("c")
print(queue.popleft(), queue[0]) # a b
print(len(stack), len(queue)) # 2 2Si quieres métodos con nombre y errores claros, envuelve las estructuras integradas en una clase pequeña:
from collections import deque
class Queue:
def __init__(self):
self._items = deque()
def enqueue(self, item):
self._items.append(item)
def dequeue(self):
if not self._items:
raise IndexError("dequeue from empty queue")
return self._items.popleft()
def __len__(self):
return len(self._items)
q = Queue()
q.enqueue("job1")
q.enqueue("job2")
print(q.dequeue(), len(q)) # job1 1Problema clásico de pilas: paréntesis balanceados
Para comprobar que cada paréntesis se cierra en el orden correcto, apila cada símbolo de apertura. Cuando veas uno de cierre, la cima de la pila debe ser su pareja de apertura.
def is_balanced(text):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in text:
if ch in "([{":
stack.append(ch)
elif ch in pairs:
if not stack or stack.pop() != pairs[ch]:
return False
return not stack
print(is_balanced("f(a[1], {b: 2})")) # True
print(is_balanced("(]")) # False
print(is_balanced("((")) # FalseLa misma idea sirve para evaluar expresiones en notación polaca inversa, convertir una recursión en un bucle y dirigir la búsqueda en profundidad: sustituye la pila de llamadas por tu propia lista.
Problema clásico de colas: búsqueda en anchura
La búsqueda en anchura (BFS) recorre un grafo nivel por nivel. Una cola lo logra: los nodos descubiertos primero se procesan primero.
from collections import deque
def bfs_order(graph, start):
seen = {start}
queue = deque([start])
order = []
while queue:
node = queue.popleft()
order.append(node)
for nxt in graph[node]:
if nxt not in seen:
seen.add(nxt)
queue.append(nxt)
return order
graph = {"A": ["B", "C"], "B": ["D"], "C": ["D"], "D": []}
print(bfs_order(graph, "A")) # ['A', 'B', 'C', 'D']Complejidad temporal y espacial
| Operación | Pila (list) | Cola (deque) | Cola (list.pop(0)) |
|---|---|---|---|
| Añadir | O(1) amortizado | O(1) | O(1) amortizado |
| Quitar | O(1) | O(1) | O(n), los elementos se desplazan a la izquierda |
| Consultar | O(1) | O(1) | O(1) |
| Espacio para n elementos | O(n) | O(n) | O(n) |
Amortizado significa que un append ocasional redimensiona el arreglo subyacente y cuesta O(n), pero promediado sobre muchas operaciones cada una es O(1).
Variantes y errores comunes
- Deque (cola de doble extremo): añade y quita por ambos extremos.
collections.dequelo es y puede actuar como pila y como cola. - Cola de prioridad: saca primero el elemento menor (o mayor), no el más antiguo. Usa
heapq; consulta la página del montículo binario. - Búfer circular: una cola de tamaño fijo guardada en un arreglo con índices que dan la vuelta.
deque(maxlen=n)descarta el elemento más antiguo cuando se llena. - Colas seguras para hilos:
queue.Queuepara hilos yasyncio.Queuepara corrutinas se bloquean mientras esperan elementos. - Una cola construida con dos pilas: apila en la pila de entrada y, cuando la de salida está vacía, pasa todo a ella. Cada elemento se mueve una sola vez, así que las operaciones son O(1) amortizado.
Preguntas frecuentes
¿Cuál es la diferencia entre una pila y una cola?
Una pila saca primero el elemento añadido más recientemente (LIFO). Una cola saca primero el más antiguo (FIFO). Ambas añaden y quitan en tiempo O(1).
¿Debo usar una list o un deque para una cola en Python?
Usa collections.deque. Quitar del principio de una list con pop(0) es O(n) porque todos los demás elementos se desplazan; deque.popleft() es O(1).
¿Dónde se usa una pila en programas reales?
La pila de llamadas que guarda las llamadas a funciones, el historial de deshacer y rehacer, el botón de retroceso del navegador, el análisis de expresiones, el emparejado de paréntesis y la búsqueda en profundidad.
¿Qué es un desbordamiento de pila (stack overflow)?
Ocurre cuando la pila de llamadas se queda sin espacio, normalmente por una recursión muy profunda o infinita. En Python aparece como RecursionError cuando se supera el límite de recursión.
¿queue.Queue es lo mismo que collections.deque?
No. queue.Queue es una cola segura para hilos, con bloqueos y get y put bloqueantes, pensada para pasar trabajo entre hilos. collections.deque es un contenedor de propósito general más rápido y sin bloqueo.