Pilas en Python

 


Una pila (stack) es una estructura de datos que sigue el principio LIFO (Last In, First Out), lo que significa que el último elemento en entrar es el primero en salir.

Concepto básico

Imagina una pila de platos:

  • Los nuevos platos se colocan en la cima de la pila

  • Los platos se retiran desde la cima de la pila

Implementaciones en Python

1. Usando listas (Recomendado para pilas)

python
# Crear una pila vacía
pila = []

# Agregar elementos a la cima (push)
pila.append("A")
pila.append("B")
pila.append("C")
print(f"Pila: {pila}")  # Pila: ['A', 'B', 'C']

# Eliminar el elemento de la cima (pop)
elemento_cima = pila.pop()
print(f"Elemento eliminado: {elemento_cima}")  # Elemento eliminado: C
print(f"Pila después de pop: {pila}")  # Pila después de pop: ['A', 'B']

# Ver el elemento de la cima sin eliminarlo
print(f"Elemento en la cima: {pila[-1]}")  # Elemento en la cima: B

2. Usando collections.deque

python
from collections import deque

# Crear una pila
pila = deque()

# Push - agregar elementos
pila.append("X")
pila.append("Y")
pila.append("Z")
print(f"Pila: {list(pila)}")  # Pila: ['X', 'Y', 'Z']

# Pop - eliminar de la cima
elemento = pila.pop()
print(f"Elemento eliminado: {elemento}")  # Elemento eliminado: Z

# Ver la cima
print(f"Cima: {pila[-1]}")  # Cima: Y

Operaciones principales

python
pila = []

# 1. Push - agregar elemento a la cima
pila.append(10)
pila.append(20)
pila.append(30)
print(f"Pila después de push: {pila}")  # [10, 20, 30]

# 2. Pop - eliminar elemento de la cima
print(f"Pop: {pila.pop()}")  # 30
print(f"Pila después de pop: {pila}")  # [10, 20]

# 3. Peek/Top - ver elemento de la cima sin eliminar
print(f"Cima: {pila[-1]}")  # 20

# 4. Tamaño de la pila
print(f"Tamaño: {len(pila)}")  # 2

# 5. Verificar si está vacía
print(f"¿Está vacía? {len(pila) == 0}")  # False

# 6. Limpiar la pila
pila.clear()
print(f"¿Está vacía después de clear? {len(pila) == 0}")  # True

Ejemplo práctico: Historial de navegación

python
class HistorialNavegacion:
    def __init__(self):
        self.pila_historial = []
        self.pila_avance = []  # Para la funcionalidad de avance
    
    def visitar_pagina(self, url):
        """Agrega una nueva página al historial"""
        self.pila_historial.append(url)
        self.pila_avance.clear()  # Limpiar pila de avance al visitar nueva página
        print(f"🌐 Visitando: {url}")
    
    def retroceder(self):
        """Retrocede a la página anterior"""
        if len(self.pila_historial) > 1:
            pagina_actual = self.pila_historial.pop()
            self.pila_avance.append(pagina_actual)
            pagina_anterior = self.pila_historial[-1]
            print(f"◀️ Retrocediendo a: {pagina_anterior}")
            return pagina_anterior
        else:
            print("🚫 No hay páginas anteriores")
            return None
    
    def avanzar(self):
        """Avanza a la página siguiente"""
        if self.pila_avance:
            pagina = self.pila_avance.pop()
            self.pila_historial.append(pagina)
            print(f"▶️ Avanzando a: {pagina}")
            return pagina
        else:
            print("🚫 No hay páginas siguientes")
            return None
    
    def pagina_actual(self):
        """Muestra la página actual"""
        if self.pila_historial:
            return self.pila_historial[-1]
        return None
    
    def mostrar_historial(self):
        """Muestra todo el historial"""
        print(f"📚 Historial: {self.pila_historial}")
        print(f"🔜 Páginas para avanzar: {self.pila_avance}")

# Uso del historial
navegador = HistorialNavegacion()

navegador.visitar_pagina("google.com")
navegador.visitar_pagina("youtube.com")
navegador.visitar_pagina("github.com")

print(f"Página actual: {navegador.pagina_actual()}")  # github.com

navegador.retroceder()  # Retrocede a youtube.com
navegador.retroceder()  # Retrocede a google.com

navegador.avanzar()     # Avanza a youtube.com

navegador.mostrar_historial()

Ejemplo: Sistema de deshacer/rehacer

python
class EditorTexto:
    def __init__(self):
        self.pila_deshacer = []
        self.pila_rehacer = []
        self.texto = ""
    
    def escribir(self, nuevo_texto):
        """Agrega texto y guarda estado anterior"""
        if self.texto != nuevo_texto:
            self.pila_deshacer.append(self.texto)
            self.texto = nuevo_texto
            self.pila_rehacer.clear()  # Limpiar rehacer al hacer nueva acción
            print(f"✏️ Texto actual: '{self.texto}'")
    
    def deshacer(self):
        """Deshace la última acción"""
        if self.pila_deshacer:
            estado_anterior = self.pila_deshacer.pop()
            self.pila_rehacer.append(self.texto)
            self.texto = estado_anterior
            print(f"↩️ Deshacer: '{self.texto}'")
        else:
            print("🚫 Nada para deshacer")
    
    def rehacer(self):
        """Rehace la última acción deshecha"""
        if self.pila_rehacer:
            estado_siguiente = self.pila_rehacer.pop()
            self.pila_deshacer.append(self.texto)
            self.texto = estado_siguiente
            print(f"↪️ Rehacer: '{self.texto}'")
        else:
            print("🚫 Nada para rehacer")

# Uso del editor
editor = EditorTexto()

editor.escribir("Hola")
editor.escribir("Hola mundo")
editor.escribir("Hola mundo Python")

editor.deshacer()  # Vuelve a "Hola mundo"
editor.deshacer()  # Vuelve a "Hola"

editor.rehacer()   # Vuelve a "Hola mundo"

Aplicaciones comunes de las pilas

  1. Evaluación de expresiones matemáticas

  2. Algoritmos de backtracking (como laberintos)

  3. Gestión de llamadas a funciones (call stack)

  4. Historial de navegación

  5. Sistemas de deshacer/rehacer

  6. Algoritmo DFS (Depth-First Search)

Ejemplo: Verificador de paréntesis balanceados

python
def verificar_parentesis(expresion):
    """Verifica si los paréntesis están balanceados"""
    pila = []
    pares = {')': '(', ']': '[', '}': '{'}
    
    for caracter in expresion:
        if caracter in '([{':
            pila.append(caracter)
        elif caracter in ')]}':
            if not pila or pila[-1] != pares[caracter]:
                return False
            pila.pop()
    
    return len(pila) == 0

# Pruebas
expresiones = [
    "((2 + 3) * 5)",
    "{[()()]}",
    "({[}])",
    "sin(x) + cos(y)",
    "((())"
]

for expr in expresiones:
    resultado = "✓ Balanceado" if verificar_parentesis(expr) else "✗ No balanceado"
    print(f"{expr} -> {resultado}")

¿Cuándo usar cada implementación?

  • Listas: Para la mayoría de casos, son eficientes para operaciones en un solo extremo

  • deque: Cuando necesitas mejor rendimiento para pilas muy grandes

  • LifoQueue: Cuando trabajas con múltiples hilos

Las pilas son estructuras fundamentales en programación, especialmente útiles para algoritmos recursivos, procesamiento de lenguaje natural, y cualquier situación donde necesites "deshacer" operaciones en orden inverso.

Comentarios

Entradas populares de este blog

¿Qué es un Closure?

4 tipos de colecciones de datos más

Funciones en Python: con y sin paréntesis