3 - Tipos de implementación de pilas en Python

Panorama general

El comportamiento LIFO se puede materializar con distintas estructuras internas. La elección depende de si buscamos simplicidad, rendimiento consistente o control didáctico. En Python las familias principales son: listas (incluyendo collections.deque) y listas enlazadas implementadas a mano.

3.1 Implementación con listas (list o deque)

Usa una secuencia ordenada para almacenar elementos y opera siempre sobre el final. Puede ser:

  • Lista nativa (list): sencilla, crece de forma dinámica y ofrece append/pop amortizados en O(1).
  • collections.deque: buffer de doble extremo con operaciones O(1) consistentes y sin realocaciones grandes.
from collections import deque

class PilaLista:
  def __init__(self, usar_deque=False, capacidad=None):
    self._capacidad = capacidad
    self._datos = deque() if usar_deque else []

  def push(self, valor):
    if self._capacidad is not None and len(self._datos) >= self._capacidad:
      raise OverflowError("Pila llena")
    self._datos.append(valor)

  def pop(self):
    if not self._datos:
      raise IndexError("Pila vacia")
    return self._datos.pop()

  def peek(self):
    if not self._datos:
      raise IndexError("Pila vacia")
    return self._datos[-1]


if __name__ == "__main__":
  pila = PilaLista(usar_deque=True)
  for n in (4, 7, 9):
    pila.push(n)
  print("Pop:", pila.pop())
  print("Pop:", pila.pop())

La lista nativa prioriza simplicidad; deque ofrece rendimiento predecible en escenarios intensivos.

Probar esta versión en el editor online:

Abrir editor online
Comparación visual entre pila con lista y con deque

3.2 Implementación con listas enlazadas

En lugar de listas nativas, se crean nodos enlazados. Cada push genera un nodo y lo enlaza al tope.

class Nodo:
  def __init__(self, valor, sig=None):
    self.valor = valor
    self.sig = sig


class PilaEnlazada:
  def __init__(self):
    self.tope = None

  def push(self, valor):
    self.tope = Nodo(valor, self.tope)

  def pop(self):
    if not self.tope:
      raise IndexError("Pila vacia")
    valor = self.tope.valor
    self.tope = self.tope.sig
    return valor


if __name__ == "__main__":
  pila = PilaEnlazada()
  for n in (1, 2, 3):
    pila.push(n)
  print("Pop:", pila.pop())
  print("Pop:", pila.pop())
  print("Pop:", pila.pop())

Esta técnica no impone capacidad fija y libera memoria a medida que se desapila; es útil con tamaños impredecibles o como apoyo didáctico.

Probar la versión enlazada en el editor online:

Abrir editor online

3.3 Diferencias entre ambas

Criterio Pila con listas/deque Pila con nodos enlazados
Reserva Contigua (list) o buffer circular (deque) No contigua, nodo a nodo
Capacidad Crece dinámicamente; se puede limitar Crece según memoria disponible
Uso de memoria Ligero overhead interno, eficiente en la práctica Overhead por enlaces, pero ajusta al número de nodos
Complejidad O(1) en append/pop; deque evita copias grandes O(1) por operación; sin redimensionado

3.4 Ventajas y desventajas prácticas

  • Listas/deque: minimalistas, sin código extra de memoria; deque ofrece O(1) estable incluso con muchas operaciones.
  • Listas enlazadas: flexibles y sin bloques grandes; requieren más líneas y validaciones manuales.
  • Elección: para la mayoría de casos usa list o deque; conserva la versión enlazada para fines académicos o cuando necesitas control total de nodos.

Conocer ambas alternativas permite ajustar la pila a las restricciones reales del proyecto y cambiar de estrategia sin reescribir la interfaz pública.