Esta guía complementa la práctica de Pilas. Cubre el TAD abstracto, las dos implementaciones de la cátedra (arreglos y punteros), y casos de uso con análisis de complejidad.
Una pila es una estructura LIFO (Last In, First Out): el último elemento en entrar es el primero en salir.
Ejemplo intuitivo: una pila de platos. Solo se puede agregar y retirar por arriba.
El TAD define qué operaciones se pueden hacer, sin imponer cómo se guarda internamente.
| Operación | Firma | Intención |
|---|---|---|
| Crear | Pila p_crear() |
Devuelve una pila vacía lista para usar. |
| Apilar | bool p_apilar(Pila, TipoElemento) |
Inserta un elemento en el tope. Devuelve false si la operación no puede realizarse. |
| Desapilar | TipoElemento p_desapilar(Pila) |
Quita y devuelve el elemento del tope, o NULL si está vacía. |
| Tope | TipoElemento p_tope(Pila) |
Devuelve el elemento del tope sin eliminarlo, o NULL si está vacía. |
| ¿Vacía? | bool p_es_vacia(Pila) |
Indica si la pila no contiene elementos. |
pilas_arreglos.c)struct PilaRep {
TipoElemento *valores;
unsigned int tope;
};
valores apunta a un arreglo contiguo en heap de tamaño fijo TAMANIO_MAXIMO.tope no es un índice válido del último elemento, sino la próxima posición libre.Si tope = 0, la pila está vacía.
Si tope = k, hay elementos válidos en valores[0..k-1].
índice: [0] [1] [2] [3] ...
valores: e1 e2 e3 --
^
tope = 3
p_crear, se reserva todo el arreglo con calloc(TAMANIO_MAXIMO, ...).p_apilar escribe en valores[tope] y luego incrementa tope.p_desapilar decrementa tope y devuelve valores[tope].p_tope accede a valores[tope - 1].p_apilar, p_desapilar, p_tope) en O(1).TAMANIO_MAXIMO = 1000).pilas_punteros.c)struct Nodo {
TipoElemento datos;
struct Nodo *siguiente;
};
struct PilaRep {
struct Nodo *tope;
};
Cada elemento se guarda en un nodo independiente y tope apunta al primer nodo de la cadena.
tope
|
v
[e3|*] -> [e2|*] -> [e1|NULL]
p_apilar crea un nodo con malloc, lo enlaza arriba y actualiza tope.p_desapilar toma el nodo de arriba, mueve tope al siguiente y libera el nodo.p_desapilar y p_tope son O(1).siguiente).$n$ = cantidad de elementos actuales de la pila.
$N$ = capacidad máxima fija (TAMANIO_MAXIMO = 1000).
| Operación | Arreglo | Punteros | Notas |
|---|---|---|---|
p_crear |
$O(N)$ | $O(1)$ | Arreglo reserva $N$ celdas; punteros solo cabecera. |
p_es_vacia |
$O(1)$ | $O(1)$ | Comparación simple (tope == 0 o tope == NULL). |
p_apilar |
$O(1)$ | $O(1)$ | Inserta directamente en el tope. |
p_desapilar |
$O(1)$ | $O(1)$ | Quita del tope. |
p_tope |
$O(1)$ | $O(1)$ | Lectura del tope sin quitar. |
Pila p = p_crear();
p_apilar(p, te_crear(10));
p_apilar(p, te_crear(20));
p_apilar(p, te_crear(30));
TipoElemento a = p_desapilar(p); // 30
TipoElemento b = p_tope(p); // 20 (queda en la pila)
Objetivo: desapilar hasta que quede vacía.
void vaciar_e_imprimir(Pila p) {
while (!p_es_vacia(p)) {
TipoElemento e = p_desapilar(p);
printf("%d\n", e->clave);
}
}
Complejidad:
| Implementación | Complejidad |
|---|---|
| Arreglo | $O(n)$ |
| Punteros | $O(n)$ |
Cada iteración hace operaciones O(1), y hay $n$ iteraciones.
Objetivo: construir una copia sin perder el contenido ni el orden de la pila original.
Estrategia común: usar una pila auxiliar temporal.
Pila copiar_pila(Pila origen) {
Pila aux = p_crear();
Pila copia = p_crear();
while (!p_es_vacia(origen)) {
TipoElemento e = p_desapilar(origen);
p_apilar(aux, e);
}
while (!p_es_vacia(aux)) {
TipoElemento e = p_desapilar(aux);
p_apilar(origen, e);
p_apilar(copia, e);
}
return copia;
}
Complejidad:
| Implementación | Complejidad |
|---|---|
| Arreglo | $O(n)$ |
| Punteros | $O(n)$ |
En ambas implementaciones se hacen una cantidad lineal de operaciones básicas sobre la pila.
Objetivo: obtener otra pila con los elementos en orden inverso.
Pila invertir_pila(Pila origen) {
Pila invertida = p_crear();
while (!p_es_vacia(origen)) {
p_apilar(invertida, p_desapilar(origen));
}
return invertida;
}
Complejidad:
| Implementación | Complejidad |
|---|---|
| Arreglo | $O(n)$ |
| Punteros | $O(n)$ |
Nuevamente, en ambos casos se realiza una secuencia lineal de operaciones sobre el tope.