2. LA PILA: UN TDA SIMPLE
• Uno de los conceptos más útiles en computación es la pila o stack
• Es un conjunto de elementos, en la que:
• Los elementos se añaden y se remueven por un solo extremo
• Este extremo es llamado “tope” de la pila
La última en llegar,
será la primera en
salir:
LAST IN, FIRST OUT
LIFO
Ejemplo:
Cuando un empleado se va de vacaciones, le llega correo a su escritorio.
Las cartas se van “apilando”.
Al regresar de vacaciones, la última carga en llegar, será la primera que revisara
Al terminar de revisarla, la nueva carta del tope de la pila habrá cambiado
Del “pilo” de cartas, la más nueva que queda, será la siguiente en ser revisada
3. TDA PILA (STACK): DEFINICION
Dada una Pila llamada S
– ¿Qué datos serían importantes conocer sobre la Pila?
– ¿Y que operaciones podríamos efectuar a la misma?
S.push(elemento1)
Elemento 1
Tope
o Cima
S.push(elemento2)
Elemento 2
S.push(elemento3)
Elemento 3
S.pop()
EstaVacia? NoSi
Al revisar c/carta, se la “sacaba” de la pila
elemento = pila.pop()
La operación pop remueve el elemento Tope de
la pila y lo retorna. La pila disminuye su tamaño
Usemos el ejemplo del correo:
Al acumularse,
Cada carta(elemento), era “metida” a la pila:
pila.push(elemento)
La operación push aumenta un elemento a la pila,
y esta aumenta en su tamaño
4. Al remover el último elemento de una pila esta queda vacía
• Una vez vacía, no se pueden “sacar” más elementos de la pila
Antes de sacar un elemento de la pila
• Debemos saber si la pila Esta Vacía?: size() == 0
El tope de la pila siempre esta cambiando
• Deberíamos poder “revisar” el elemento tope de la pila: peek()
• Si la pila esta vacía, no debe existir un valor tope
El tratar de remover elementos o acceder a elementos de una
pila vacía se llama
• SUBDESBORDAMIENTO de la pila
TDA PILA (STACK): DEFINICION
5. PILA: OPERACIONES
size() retorna -> int
Efecto: Devuelve el tamaño
push(elemento)
Efecto: Aumenta el tamaño de la pila, poniendo
el elemento en la cima (tope)
pop() retorna -> elemento
Efecto: Remueve el elemento tope y lo retorna
Excepcion: Si la pila esta vacía, produce error
peek() retorna -> elemento
Efecto: Devuelve el elemento tope de la pila
Excepcion: Si la pila esta vacía produce error
6. TDA PILA: DEFINICION FORMAL
• En conclusión:
– La pila es un conjunto de elementos
– De los cuales solo conozco y puedo ver el TOPE
• Cada elemento en la pila
– Puede contener información de cualquier tipo, es decir, es genérico
<pila> ::= <tope> + {<nodo>}
<tope> ::= <enlace>
<enlace> ::= (<Nodo> | NULL)
<nodo> ::= <contenido> + <enlace>
<contenido> ::= <<dato>>
7. TDA PILA: IMPLEMENTACION
Hay varias formas, analicemos la más sencilla
La Pila es una Lista… pero limitada
– En la lista los nuevos nodos se pueden insertar y remover:
Al/del Inicio, al final, dada una posición, etc.
– En la Pila los elementos solo se pueden:
• insertar al final y solo se pueden remover del final.
• insertar al inicio y solo se pueden remover del inicio.
Las implementaciones
– Estática
– Dinámica
8. IMPLEMENTACION ESTATICA
Pila basada en arreglos
Las operaciones sobre el arreglo
– push
– pop
– peek
MAX_ELEM
Tope = -1
Tope =
MAX_ELEM-1
Tope = 0
11. IMPLEMENTACION DINAMICA
• Una pila, o una cola, las dos son Listas realmente
– Listas en las cuales las operaciones en Insertar y Remover
están limitadas
• Una pila, se implementa exactamente igual que una Lista
– Al hacer push, es InsertarNodoFinal (o inicio)
– Al hacer pop, es SacarNodoFinal (o inicio)
13. EJEMPLO
• Las pilas se usan para
– Recuperar un conjunto de elementos en orden inverso a
como se introdujeron
• Ejemplo: Un programa debe
– Leer una secuencia de elementos enteros
– Luego mostrarlos en orden inverso al ingresado
• Si se ingresa: 1, 3, 5, 7
• Se muestra: 7, 5, 3, 1
14. ANALISIS
• Cada elemento ingresado puede ser “metido” en la pila
• Ejemplo:
1
3
5
7
1
3
5
1
3
1
7 5 3 1
Una vez llenada la pila,
Solo hay que “sacar”, elemento
tras elemento
Hasta que la pila quede vacía
15. SOLUCION
Stack<Integer> pila = new Stack( );
System.out.println("Ingrese numero");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while(n!= centinela){
pila.push(n);
System.out.println("Ingrese numero");
n = sc.nextInt();
}
while(! pila.empty( )){
System.out.println(pila.pop( ));
}
16. UN APLICACION MAS PRACTICA
• El compilador siempre sabe cuando se ha escrito un
paréntesis, o una llave de más
• (a+b)) Mal
• ((a+b) * c / 4*g-h) OK
• Con el uso de pilas, se puede reconocer los paréntesis que no
coinciden
• ¿Como lograr esta aplicación de la pila?
17. • Cuando revisamos una expresión de este tipo:
– Revisamos los paréntesis de la derecha, y buscamos si tienen “match” en la
izquierda
Para seguir este enfoque podríamos:
“Recordar” los paréntesis de la izquierda ( a medida que aparecen:
El primero en aparecer, será el último en ser “recordado”
El último en aparecer, será el primero en ser “recordado”
La Pila se utiliza
justamente para
“recordar” de la forma
abajo indicada
7 - ((X* ((X+Y)/(J-3)) + Y) / (4-2.5))
Así, cuando aparece un )
El primer ( recordado, debe ser su “match”
En ese momento, este primero recordado, ya puede ser “olvidado”
Al llegar al final de la expresión, ningún ( debería ser “recordado”
7 - ((X* ((X+Y)/(J-3)) + Y) / (4-2.5))
ANALISIS DEL PROBLEMA
18. APLICANDO PILAS
Revisemos la expresión anterior
Todos los ),
encontraron su (
7 - ( ( X *( ( X+Y) / ( J - 3) ) +Y) / ( 4- 2) )
(
(
(
(
19. Y AHORA, EL ALGORITMO
Stack<String> s = new Stack();
int i =0;
while(i<cadena.length())//Mientras no hayamos leido toda la cadena
{
//evaluar el siguiente simbolo de la expresion
if(cadena.charAt(i)=='(')
s.push('('); /*Almacenarlo*/
if(cadena.charAt(i)== ')'){ /*Buscar match*/
if(s.empty()) /*No hubo match!!*/
return false
s.pop();
}i++;
}
if(s.empty())
return true;
else
/*Algun simbolo se quedo dentro, porque no hubo match*/
return false;
20. • Una expresión aritmética:
– Conjunto de operadores, variables y paréntesis. Ejemplo:
• A+B
• Esta forma de escribir las expresiones: NOTACION INFIJA
• El operador siempre va en medio de los operandos
• En una expresión, las operaciones se “ejecutan” en un cierto
orden
– A+B*C no es igual que (A+B)*C
– Cada operador tiene su nivel de precedencia, recordemos:
• Paréntesis : () Mayor prioridad
• Potencia : ^
• Multiplicación/división: *,/
• Suma/Resta : +,- Menor Prioridad
APLICANDO PILAS
21. NOTACIONES
• La notación infija es la más popular
• No es la única forma, hay dos más
– NOTACION PREFIJA(POLACA)
• +AB Aquí el operador va antes que
los operandos
– NOTACION POSFIJA(POLACA INVERSA)
• AB+ Aquí el operador va después
que los operandos
• Siempre tener en cuenta la precedencia de
los operadores
• Ejemplo. Pasar a postfija las siguientes
expresiones:
Agrupar como establece la precedencia
(A+B)*C
Convertir operación por operación
La de mayor precedencia primero
(AB+)*C
La que le sigue en precedencia
(AB+)C*
Remover Paréntesis
AB+C*
Ya no se necesitan paréntesis
En postfija, el orden de los
operadores es el verdadero
orden de ejecución
A+B*C
(A+B)*C
Agrupar como establece la precedencia
A+(B*C)
Convertir operación por operación
La de mayor precedencia primero
A+(BC*)
La que le sigue en precedencia
A(BC*)+
Remover Paréntesis
ABC*+
22. EVALUACION DE EXPRESIONES POSFIJAS
• Dadas
– AB+C*
– ABC*+
– Evalúelas, cuando A = 3, B = 4 y C = 5
– La primera, resultado : 35
– La segunda, resultado: 23
• Que algoritmo se siguió para evaluar estas expresiones?
AB+C* ABC*+
A+B -> 7
7*C -> 35
B*C -> 20
A+20 -> 23
7C* A20+
23. EVALUACION: ALGORITMO
• Con lo anterior, ya tenemos una idea de que hacer
– Deberíamos poder “recordar” c/operando de la expresión
– Si encontramos un operador
• Los dos últimos operandos recordados son los usados y “olvidados”
• El resultado de la operación, debe ser también “recordado”
– Así, hasta que la expresión termine
Podría ser un una pila
2 veces Pop
Push del
resultado en la
pila
ABC*+ B * C+A
A
B
C
B*C
24. EN PSEUDOCODIGO
Stack s;
while(no hayamos revisados toda la expresion)
{
simbolo = siguiente elemento
if(simbolo es un operando)
s.push( símbolo );
else{
operando2 = s.pop( );
operando1 = s.pop( );
valor = resultado de operación símbolo entre
operando1 y operando2
s.push( valor );
}
}
return s.pop( );
25. Pero aun no podemos continuar. Seguimos
comparando el – con el de mayor prioridad
hasta ahora, el +. Como el – no tiene mayor
prioridad que el +, el + ya puede ser añadido
a la expresión. Como ya no queda mas en la
pila,
El – es definitivamente hasta ahora, el de
“mayor prioridad”, debemos recordarlo
Comparado con el de mayor
prioridad hasta ahora(el *), el –
no tiene mayor prioridad.
Ahora si podemos decir, que el *
es el operador de mayor
prioridad
Podemos añadir el * a la nueva
expresion, y “olvidarnos” de el
Aquí terminamos de revisar la
expresión, símbolo por símbolo.
En la pila, quedan aun
operadores.
Todos se sacan y se añaden a la
nueva expresión
Así termina la conversión
+ es un operador, como la
pila esta vacía, mejor,
guardarlo
* Es un operador. Si se
compara con el ultimo
recordado, el * tiene
mayor prioridad. Se
guarda
CONVERSION DE INFIJA A POSFIJA
A + B * C - D A B C * + D -
El operador de mayor
precedencia en la expresión será
el primero en aparecer en la
conversión
A
El operador de
mayor
precedencia es el
primero en
aparecer en la
expresión
A es un operando,
es añadido
directamente a la
nueva expresión en
postfija
+
*
-
B C *+ D -
26. CONVERSION: ALGORITMO
• Cada símbolo de la expresión es revisado
• Si el símbolo es un operando,
– Se añade a la expresión
• Si el símbolo es un operador
– El símbolo es evaluado con respecto a su prioridad
– Si tiene mayor prioridad que el último operador almacenado
• Aún no se puede decir nada, y se recuerda, es decir, se almacena en un pila
– Si no tiene mayor prioridad que el último operador almacenado
• El último operador almacenado se saca y se añade a la nueva expresión
• Esto sigue hasta que el operador que estamos revisando sea el de mayor
prioridad de todos los almacenados en la pila
• Una vez revisados todos los símbolos de la expresión
– Si hay algo almacenado en la pila, se saca y se añade a la nueva expresión
27. EN PSEUDOCODIGO
Stack s;
s.clear( );
while(no termine la expresión en infija)
{
símbolo = siguiente carácter de entrada;
if(símbolo es un operando)
añadir símbolo a la nueva expresión posfija
else{
while(símbolo tenga menor o igual precedencia que el tope de la pila)
{
simb_tope = s.pop( );
añadir simb_tope a la nueva exp.
}
s.push( símbolo )
}
}
/*le da salida a los operadores restantes*/
while( !s.empty( ) ){
simb_tope = s.pop( );
añadir simb_tope a la nueva exp. posfija
}
28. Y CON PARENTESIS
• Lo anterior, es valido para conversión de expresiones
sin paréntesis
• Para resolver este problema, podemos seguir las
siguientes reglas:
– Los paréntesis izquierdos (
• Siempre van a ser añadidos a la pila, pase lo que pase
– Los paréntesis derechos )
• Significa que un ambiente de () ha sido terminado,
• Todos los operadores de la pila, se sacan, hasta encontrar un (
29. CON PARENTESIS: ALGORITMO
Stack s;
s.clear( );
while(no termine la expresión en infija){
símbolo = siguiente carácter de entrada;
if(símbolo es un operando)
añadir símbolo a la nueva expresión posfija
else{
if(símbolo == ‘)’){
while( true ){
simb_tope = s.pop( );
if (simb_tope == ‘(’ || s.empty( )) break;
añadir simb_tope a la nueva exp.
}
}
else if(simbolo != ‘(’ ){
while(simbolo tenga menor o igual precedencia que el tope de la pila)
simb_tope = s.pop( );
añadir simb_tope a la nueva exp.
}
}
s.push( símbolo )
}
}
/*le da salida a los operadores restantes*/
while( !s.empty( ) )){
simb_tope = s.pop( );
añadir simb_tope a la nueva exp. posfija
}
30. Escribir el método estático eliminarBase que tenga como argumento una pila
de enteros. El método debe retornar el elemento situado en la base (o fondo)
de la pila. Además, la pila debe quedar con los elementos originales sin la
base.
Escribir el método estático reemplazarElementos que tenga como
argumentos una pila de enteros y dos números (buscar y reemplazo), de
forma que todas las apariciones del número 'buscar' en la pila sean
reemplazadas por el número de reemplazo.
TAREA
• Stack<Integer> pila = new Stack( );
• pila.push(n);
• n = pila.pop( );
• while(! pila.empty( ))
System.out.println( pila.pop( ));