Sunteți pe pagina 1din 9

Capı́tulo 4

Pilas

Las pilas son estructuras muy sencillas y poderosas, también conocidas como LIFO (last in,
first out) por la forma en que se trabaja con ellas. Ejemplo de ellas son las pilas de charolas
en una panaderı́a, donde sólo se puede tomar la charola que está arriba de pila y si se quiere
depositar una charola se debe hacer arriba de las demás.

4.1. Motivación
Se tienen tres palos o postes, en el primero de ellos se tienen n discos de diferente tamaño
cada uno. El disco de mayor diámetro se encuentra en el fondo del palo y el de diámetro
menor hasta arriba, con la restricción que ningún disco de tamaño mayor puede colocarse
sobre uno menor. Los otros dos palos están vacı́os. El problema consiste en pasar los discos
del primer palo al segundo, usando el tercer palo como auxiliar. Este es un ejemplo de uso
de una pila.
Si el número de discos es tres, la solución al problema consta de los siguientes pasos:

Mover el disco 1 del palo A al 2


Mover el disco 2 del palo A al 3
Mover el disco 1 del palo B al 3
Mover el disco 3 del palo A al 2
Mover el disco 1 del palo C al 1
Mover el disco 2 del palo C al 2
Mover el disco 1 del palo A al 2

¿Como son los movimientos de los discos? Siempre se toma el disco de más arriba y
se coloca hasta arriba del palo al que se desea pasar. Esto da pie a una estructura de
datos denominada pila. Una pila es una estructura con la restricción que las inserciones y
supresiones son siempre por el mismo lado, denominado tope.

1
CAPÍTULO 4. PILAS 2

Se puede observar que para 3 discos se requieren 7 movimientos. En general para n discos
se requieren 2n − 1 movimientos. Existe una leyenda que dice que en la ciudad de Hanoi
hay unos monjes dedicados a resolver el problema con 64 discos. (De ahı́ que este problema
se conoce como el problema de las torres de Hanoi.) Si mover cada disco les toma un dı́a,
entonces resolver el problema tomará 264 − 1 dı́as. La parte fea de la historia es que el dı́a
que resuelvan el problema se terminará el mundo :)
Aunque el problema, no usa pilas en su solución programada se incluye aquı́ porque es
un ejemplo ilustrativo del concepto de pila y se presenta como un ejemplo de recursión más
elaborada.
La solución al problema consiste en pasar primero el disco1 del palo1 al palo2. Entonces
el problema se reduce a pasar los n − 1 discos restantes del palo1 al palo3 y finalmente
regresar esos n − 1 discos del palo3 al palo2. A continuación la implementación recursiva
de la solución.

/** Programa que juega a las torres de Hanoi.


* @version Septiembre 2004.
*/
public class Hanoi {
private final int tamanio;

public Hanoi () {
System.out.println("Torres de Hanoi");
tamanio = 3;
solucion(tamanio, ’a’,’b’,’c’);
}

public Hanoi (int tam) {


System.out.println("Torres de Hanoi");
tamanio = tam;
solucion(tamanio, ’a’,’b’,’c’);
}

private void solucion (int n, char a, char b, char c) {


if (n == 1)
System.out.println("Mover el disco "+n+" del palo "+a+" al palo "+b);
else{
solucion(n-1, a, c, b);
System.out.println("Mover el disco "+n+" del palo "+a+" al palo "+b);
solucion(n-1, c, b, a);
}


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 3

static public void main (String [ ] args) {


Hanoi han = new Hanoi();
}
}

4.2. Implementación
Una pila es una lista con la restricción que las inserciones y supresiones son siempre por
el mismo lado, denominado tope.
La interfaz para el manejo de este tipo de datos es la siguiente:

interfaz Apilable {
public void push(Object);
public void pop();
public Object top();
}

Las operaciones básicas con las pilas son:

push. Inserta el elemento al frente de la pila.

pop. Elimina el elemento que se insertó al último. Si la pila está vacı́a causa un error.

top. Regresa el valor del elemento que se insertó al último. Si la pila está vacı́a causa
un error (NoSuchElementException). Con este método no se altera el estado de la
pila.

estáVacı́a. Para saber si la pila está vacı́a.

limpiar. Para vaciar el contenido de una pila.

A continuación la implementación de las operaciones para pilas usando nodos ligados.

public class Pila extends Apilable {


private Nodo tope;

/**
* Construye la pila.
*/
public Pila() { tope = null; }


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 4

/**
* Verifica que la pila esté llena. En esta implementación
* @return false siempre.
*/
public boolean estáLlena() { return false; }

/**
* Verifica que la pila esté vacı́a.
* @return true si lo está y falso en otro caso.
*/
public boolean estáVacı́a (){ return tope == null; }

/**
* Vacı́a una pila.
*/
public void vaciaPila() { tope = null; }

/**
* Devuelve el elemento del tope de la pila (sin alterar ésta)
* o bien null si se encuentra vacı́a.
*/
public Object top() {
return (estáVacı́a()) ? null : tope.elemento;
}

/**
* Extrae el elemento del tope de la pila.
* Devuelve null si la pila está vacı́a.
*/
public Object pop() {
if (estáVacı́a())
return null;

Object dato = tope.elemento;


tope = tope.sgte;
return dato;
}

/**


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 5

* Inserta un nuevo elemento en la pila.


* @param x el elemento a insertar.
*/
public void push(Object x) {
tope = new Nodo(x, tope);
}
/**
* Iterador para conseguir todos los elementos de la pila sin alterarla.
*/
public java.util.Iterator elementos()
{ return new MiIterador(); }

private class MiIterador implements java.util.Iterator {


private Nodo posicion = tope;

public boolean hasNext() { return tope != null;}

public Object next() { //throws NoSuchElementException {


if (hasNext()) {
Object o = posicion.elemento;
posicion = posicion.sgte;
return o;
}
// throw new NoSuchElementException();
return null;
}

public void remove() {


throw new IllegalStateException();
}
}}
El tiempo que toma cada una de las operaciones sobre pilas con la implementación anterior
es constante y no depende del tamaño de la pila.

4.3. Balanceo de elementos


A pesar de ser muy pocas las instrucciones sobre una pila, éstas son además de eficientes
muy poderosas. En esta sección se presenta un problema que requiere en su solución el uso
de una pila.


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 6

El problema consiste en determinar si una expresión aritmética tiene bien anidados sus
paréntesis. Considerando tres tipos de éstos. Es decir, que no ocurra que se tengan más
paréntesis de un tipo que de otro y que cierren adecuadamente. Por ejemplo,
(a+[b*c]/[d-h])*25 es correcta
{a+{b+c+{d+e}+e}} es correcta
{a+{b+c+{d+e}+e} es incorrecta
((a+b} es incorrecta
El algoritmo es el siguiente:
1. Crear una pila vacı́a.

2. Leer cada elemento de la cadena, para cada uno hacer:

a) Si el caracter es un sı́mbolo que abre ([{, meterlo a la pila.


b) Si es uno que cierra y la pila está vacı́a, reportar un error.
c) Si es uno que cierra y la pila no está vacı́a, sacar de la pila un caracter y verificar
que corresponda con el sı́mbolo que abre. Si no es ası́ reportar un error.
d ) Cualquier otro caracter ignorarlo.

3. Si la pila no está vacı́a reportar un error.


La programación del algoritmo anterior se presenta a continuación:
public class Parentesis {
Pila pila = new Pila();

public static void main (String [ ] args) {


Parentesis linea = new Parentesis(args[0]);
}

public Parentesis (String linea) {


for (int i = 0; i < linea.length(); i++)
if (linea.charAt(i) == ’(’) pila.push(new Character(’)’));
else if (linea.charAt(i) == ’{’) pila.push(new Character(’}’));
else if (linea.charAt(i) == ’[’) pila.push(new Character(’]’));
else if (linea.charAt(i) == ’)’) verifica(’)’);
else if (linea.charAt(i) == ’}’) verifica(’}’);
else if (linea.charAt(i) == ’]’) verifica(’]’);

if (pila.estáVacı́a())


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 7

System.out.println("Parentesis balanceados");
else reportaError();
}

private void verifica (char c) {


if (pila.estáVacı́a())
reportaError();
Character s = pila.pop();
if (c != s.charValue())
reportaError();
}

private void reportaError() {


System.out.println("Parentesis NO balanceados");
System.exit(1);
}
}

4.4. Conversión de infija a postfija


Si se tiene la expresión A+B se piensa “sumar A con B”, es decir primero se piensa la
operación y luego los operandos. La notación usual se conoce como infija (A+B) y la que
pensamos, prefija (+AB). Existe otra notación que se denomina postfija en la cual el operador
va después de los operandos. Por ejemplo, AB+.
Ejemplos de expresiones en notación postfija son:

A+B*C --> A+(BC*) --> A(BC*)+ --> ABC*+


(A+B)*C --> (AB+)*C --> (AB+)C* --> AB+C*
A+B-C --> AB+C-
(A+B)*(C-D) --> AB+CD-*
((A+B)*C-(D-E))^(F+G) --> AB+C*DE--FG+^

Tener la expresión de esta forma, facilita su evaluación como se verá en la siguiente sección.
Para simplificar este algoritmo sólo se utilizan los operadores de suma, multiplicación y
paréntesis, con las reglas de precedencia de siempre y se asume que la expresión es correcta.
Ejemplos:

a + b * c + (d * e + f) * g ----> abc*+de*f+g*+

El algoritmo de conversión empieza teniendo una pila vacı́a y una expresión, en notación
infija, correcta.


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 8

1. Leer un elemento.
2. Mientras haya elementos, hacer los pasos a al e:

a) Si se tiene un operando se coloca directamente en la salida. (El orden de estos no


se altera).
b) Si es el primer operador se debe colocar en la pila incluyendo un paréntesis iz-
quierdo.
c) Si es un paréntesis derecho se sacan de la pila los operadores que ahı́ se encuentren
y se van colocando en la salida cada uno, hasta encontrar el paréntesis izquierdo
correspondiente, el cual se saca de la pila pero no se coloca en la salida.
d ) Si es un operador diferente de paréntesis derecho, se sacan de la pila todos los
operadores de mayor o igual prioridad que el que se tienen en la mano, excepto
el paréntesis izquierdo. Al finalizar se coloca el operador en la pila.
e) Leer el siguiente elemento

3. Se sacan de la pila todos los operadores.


En este algoritmo la pila representa los operadores pendientes.
El algoritmo es de orden O(n). Una pequeña observación a-b-c se convierte en ab-c- lo
cual es correcto debido a que la asociación es de izquierda a derecha. Como no es el caso de
la exponenciación, no funcionarı́a este algoritmo para ese operador.

4.5. Evaluación de expresiones


Una vez que se tiene una expresión aritmética en notación postfija su evaluación es muy
sencilla y también requiere de una pila. El algoritmo es el siguiente:

1. Inicia con una pila vacı́a


2. Se lee cada sı́mbolo de la expresión. Para cada uno hacer:
a) Si el sı́mbolo es un operando se mete a la pila.
b) En caso contrario, se trata de un operador. En este caso se sacan los dos elementos
del tope de la pila; se realiza la operación y el resultado de la misma se coloca en
la pila.
op2 = p.pop();
op1 = p.pop(),
valor = op1 simbolo op2;
p.push(valor);


c Amparo López Gaona, 2006
CAPÍTULO 4. PILAS 9

3. Finalmente se saca de la pila el resultado.

Ejemplo, seguir el algoritmo con la siguiente expresión: 6 2 3 + - 3 8 2 / + * 2 ^


3 +

4.6. Juego de cartas


c Amparo López Gaona, 2006

S-ar putea să vă placă și