Pilas en Java
Pilas en Java
Pilas en Java
STACK
La clase Stack es una clase de las llamadas de tipo LIFO (Last In - First Out, o
último en entrar - primero en salir). Esta clase hereda de la clase que ya hemos
estudiado anteriormente en el curso Vector y con 5 operaciones permite tratar
un vector a modo de pila o stack.
Las operaciones básicas son push (que introduce un elemento en la pila), pop
(que saca un elemento de la pila), peek (consulta el primer elemento de la cima
de la pila), empty (que comprueba si la pila está vacía) y search (que busca un
determinado elemento dentro de la pila y devuelve su posición dentro de ella).
Esta clase es muy sencilla y al crear un objeto de tipo Stack con el constructor
básico evidentemente no contendrá ningún elemento.
Realizaremos un ejemplo a modo de uso de pila. Uno de los casos más usados
en informática de una pila es el de querer verificar si una determinada sentencia
o instrucción está equilibrada en cuanto a número de paréntesis, corchetes o
llaves de apertura y cierre. Cuando se escribe código de programación si no
existe equilibrio entre signos de apertura (por ejemplo un paréntesis de
apertura) y cierre (por ejemplo un paréntesis de cierre) ni siquiera debería
procesarse la sentencia ya que no estaría formalmente bien construida. De esto
se encargan los analizadores léxicos de los compiladores.
Así que vamos a utilizar esta vez tan solo una clase Programa con el método
main, donde vamos a ir analizando una sentencia para verificar si es equilibrada
o no en símbolos de paréntesis, recorriendo todos sus caracteres desde el inicio
hasta el final.
Iremos construyendo nuestra pila apilando un símbolo (cada vez que detectemos
un símbolo de apertura o desapilando de ella cuando detectemos un símbolo de
cierre. Tendremos que ir analizando todos los caracteres de una expresión y
actuar cuando detectemos un paréntesis, operando en función de si el paréntesis
leído es de abrir (“(”) o cerrar (“)”). El equilibrio en la escritura vendrá
determinado al terminar el análisis en función de si la pila está vacía (hay
equilibrio) o contiene algún elemento (no hay equilibrio).
Tendremos que tener en cuenta casos especiales como una expresión cuyo
primer elemento sea un paréntesis de cierre. Por ejemplo: “Hay varios países
)México, España(“ la consideraríamos una expresión incorrecta ya que si la pila
está vacía el primer elemento siempre tendrá que ser un paréntesis de apertura
y no uno de cierre. Tendremos en cuenta por tanto que además de equilibrio
exista corrección en la forma de construcción (que no puedan existir cierres de
paréntesis que no se hayan abierto).
import java.util.Stack;
public class Programa {
public static void main(String arg[]) {
String cadenano = "(Cadena no equilibrada en paréntesis(()()()))))";
String cadenasi = "(Cadena equilibrada en parentesis())";
System.out.println("Verificación equilibrado en paréntesis para cadenano:");
System.out.println(verificaParentesis(cadenano));
System.out.println("Verificación equilibrado en paréntesis para cadenasi:");
System.out.println(verificaParentesis(cadenasi));
}
El diagrama de clases por tanto para BlueJ es muy sencillo y tiene tan solo la
clase Programa:
Hemos visto un claro ejemplo del uso de la clase Stack que aunque muy sencilla,
es muy útil ya que su implementación es muy fácil de aprender con tan solo los
5 métodos comentados anteriormente (push, pop, peek, empty, search).
Una pila (stack) es una colección ordenada de elementos a los que sólo se puede
acceder por un único lugar o extremo de la pila. Al primer elemento agregado
se lo denomina como "fondo", mientras que al último se lo conoce como
"cima". Los elementos de la pila se añaden o se borran de la misma sólo por su
parte superior (cima).
APILAR (PUSH):
Consiste en añadir un elemento a la pila. En la siguiente imagen se explica de
una forma más abstracta en la que se puede ver que se está añadiendo el dato
"7" sobre el dato "8", convirtiéndose éste en la "cima" de la pila.
DESAPILAR (POP):
Es la operación contraria de "apilar", es decir, en lugar de añadir elementos a la
pila, se los va a quitar de la misma. En la siguiente imagen se explica de una
forma más abstracta en la que se puede ver que se está quitando de la pila el
dato "7" establecido anteriormente como la "cima".
EJEMPLO PRÁCTICO:
Clase pilas
package estructurasDeDatos;
public Pila(){
tamanioPila = 1;
top = -1;
pila = new int[tamanioPila];
}
Clase principal
package estructurasDeDatos;
pila.push(10);
pila.push(20);
pila.push(30);
System.out.println(pila.pop());
System.out.println(pila.pop());
System.out.println(pila.pop());
System.out.println(pila.pop());
}
Una pila es una estructura de datos del tipo LIFO, con dos operaciones imprescindibles:
apilar (push) y desapilar (pop), al primer elemento se lo conoce como "fondo" y al último
como "cima".
Aplicaciones de las Pilas
Cuando un programa llama a un subprograma, internamente, se utilizan pilas
para guardar el lugar desde donde se hizo la llamada, y el estado de las variables
en ese momento.
Otro ejemplo es que todo algoritmo recursivo, puede ser implementado en forma
iterativa utilizando una pila para almacenar los valores de las variables y
parámetros.
Código Pila
import java.util.Stack;
}
EXPRESIONES InFija, PreFija Y PosFija
import java.util.Stack;
/**
*
* @author Administrador
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
//Entrada (Expresión en Postfija)
String expr = "2 23 6 + * 1 -"; // equivale a 2*(23+6)-1
String[] post = expr.split(" ");
//Mostrar resultados:
System.out.println("Expresion: " + expr);
System.out.println("Resultado: " + P.peek());
Clase Nodo:
package pilas;
/**
* Clase que define los elementos que debe tener un Nodo de
la lista.
* @author xavier
*/
public class Nodo {
// Variable en la cual se va a guardar el valor.
private int valor;
// Variable para enlazar los nodos.
private Nodo siguiente;
/**
* Constructor que inicializamos el valor de las
variables.
*/
public void Nodo(){
this.valor = 0;
this.siguiente = null;
}
package pilas;
/**
* Clase que define las operaciones básicas que debe tener
una pila.
* @author xavier
*/
public class Pila {
// Puntero que indica el inicio de la pila o tambein
conocida como el
// tope de la pila.
private Nodo inicio;
// Variable para registrar el tamaño de la pila.
private int tamanio;
/**
* Constructor por defecto.
*/
public void Pila(){
inicio = null;
tamanio = 0;
}
/**
* Consulta si la pila esta vacia.
* @return true si el primer nodo (inicio), no apunta a
otro nodo.
*/
public boolean esVacia(){
return inicio == null;
}
/**
* Consulta cuantos elementos (nodos) tiene la pila.
* @return numero entero entre [0,n] donde n es el numero
de elementos
* que contenga la lista.
*/
public int getTamanio(){
return tamanio;
}
/**
* Agrega un nuevo nodo a la pila.
* @param valor a agregar.
*/
public void apilar(int valor){
// Define un nuevo nodo.
Nodo nuevo = new Nodo();
// Agrega al valor al nodo.
nuevo.setValor(valor);
// Consulta si la pila esta vacia.
if (esVacia()) {
// Inicializa la pila con el nuevo valor.
inicio = nuevo;
}
// Caso contrario agrega el nuevo nodo al inicio de
la pila.
else{
nuevo.setSiguiente(inicio);
inicio = nuevo;
}
// Incrementa el contador del tamaño.
tamanio++;
}
/**
* Elimina el elemento que se encuentra en el tope de la
piala.
*/
public void retirar(){
if (!esVacia()) {
// Asigna como primer nodo al siguiente de la
pila.
inicio = inicio.getSiguiente();
// Decrementa el contador del tamaño de la pila
tamanio--;
}
}
/**
* Consulta el valor del nodo que se encuentra en la cima
de la pila
* @return valor del nodo.
* @throws Exception
*/
public int cima() throws Exception{
if(!esVacia()){
return inicio.getValor();
} else {
throw new Exception("La pila se encuentra
vacia.");
}
}
/**
* Busca un elemento en la pila.
* @param referencia valor del nodo a buscar.
* @return true si el valor de referencia existe en la
pila.
*/
public boolean buscar(int referencia){
// Crea una copia de la pila.
Nodo aux = inicio;
// Bandera para verificar si existe el elemento a
buscar.
boolean existe = false;
// Recorre la pila hasta llegar encontrar el nodo o
llegar al final
// de la pila.
while(existe != true && aux != null){
// Compara si el valor del nodo es igual que al
de referencia.
if (referencia == aux.getValor()) {
// Cambia el valor de la bandera.
existe = true;
}
else{
// Avanza al siguiente nodo.
aux = aux.getSiguiente();
}
}
// Retorna el valor de la bandera.
return existe;
}
/**
* Elimina un nodo de la pila ubicado por su valor.
* @param referencia valor de referencia para ubicar el
nodo.
*/
public void remover(int referencia){
// Consulta si el valor existe en la pila.
if (buscar(referencia)) {
// Crea una pila auxiliar para guardar los
valores que se
// vayan desapilando de la pila original.
Nodo pilaAux = null;
// Recoore la pila hasta llegar al nodo que tenga
el valor
// igual que el de referencia.
while(referencia != inicio.getValor()){
// Crea un nodo temporal para agregarlos a la
pila auxiliar.
Nodo temp = new Nodo();
// Ingresa el valor al nodo temporal.
temp.setValor(inicio.getValor());
// Consulta si la pila auxiliar no a sido
inicializada.
if(pilaAux == null){
// Inicializa la pila auxiliar.
pilaAux = temp;
}
// Caso contrario si la pila auxiliar ya
contiene elementos
// los agrega al inicio.
else{
temp.setSiguiente(pilaAux);
pilaAux = temp;
}
// Elimina el nodo del tope de la pila hasta
llegar al nodo
// que se desea eliminar.
retirar();
}
// Elimina el nodo que coincide con el de
referencia.
retirar();
// Regresa los valores de la pila auxiliar a la
pila original
// mientras la pila auxiliar tenga elementos.
while(pilaAux != null){
// Utiliza el metodo apilar para regresar los
elementos a
// la pila original.
apilar(pilaAux.getValor());
// Avansa al siguiente nodo de la pila
auxiliar.
pilaAux = pilaAux.getSiguiente();
}
// Libera la memoria utilizada por la pila
auxiliar.
pilaAux = null;
}
}
/**
* Actualiza el valor de un nodo en la pila.
* @param referencia valor del nodo para ubicar el que se
desea actualizar.
* @param valor por el cual se desea remplazar el valor
del nodo.
*/
public void editar(int referencia, int valor){
// Consulta si el nodo existe en la pila
if (buscar(referencia)) {
// Crea una pila auxiliar.
Nodo pilaAux = null;
// Recoore la pila hasta llegar al nodo que tenga
el valor
// igual que el de referencia.
while(referencia != inicio.getValor()){
// Crea un nodo temporal para agregarlos a la
pila auxiliar.
Nodo temp = new Nodo();
// Ingresa el valor al nodo temporal.
temp.setValor(inicio.getValor());
// Consulta si la pila auxiliar no a sido
inicializada.
if(pilaAux == null){
// Inicializa la pila auxiliar.
pilaAux = temp;
}
// Caso contrario si la pila auxiliar ya
contiene elementos
// los agrega al inicio.
else{
temp.setSiguiente(pilaAux);
pilaAux = temp;
}
// Elimina el nodo del tope de la pila hasta
llegar al nodo
// que se desea eliminar.
retirar();
}
// Actualiza el valor del nodo.
inicio.setValor(valor);
// Regresa los valores de la pila auxiliar a la
pila original
// mientras la pila auxiliar tenga elementos.
while(pilaAux != null){
// Utiliza el metodo apilar para regresar los
elementos a
// la pila original.
apilar(pilaAux.getValor());
// Avansa al siguiente nodo de la pila
auxiliar.
pilaAux = pilaAux.getSiguiente();
}
// Libera la memoria utilizada por la pila
auxiliar.
pilaAux = null;
}
}
/**
* Elimina la pila
*/
public void eliminar(){
// Elimina el valor y la referencia a los demas
nodos.
inicio = null;
// Reinicia el contador a 0.
tamanio = 0;
}
/**
* Despliega en pantalla los elementos de la pìla.
*/
public void listar(){
// Crea una copia de la pila.
Nodo aux = inicio;
// Recorre la pila hasta el ultimo nodo.
while(aux != null){
System.out.println("|\t" + aux.getValor() +
"\t|");
System.out.println("-----------------");
aux = aux.getSiguiente();
}
}
}
Clase Main
package pilas;
/**
* Clase principa que implementa los metodos de la clase
pila.
* @author xavier
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Pila pila = new Pila();
pila.apilar(4);
pila.apilar(16);
pila.apilar(12);
pila.apilar(8);
pila.apilar(65);